likes
comments
collection
share

Vue3: JSX 更灵活的开发场景

作者站长头像
站长
· 阅读数 62

Vue2应对灵活场景的方法:Render函数

如果某个组件比较灵活,用一般的template模版结构无法满足需求,或者实现起来很不优雅,在Vue2时代,我们只能手写render函数。

Vue2介绍render函数时,有一个例子:通过一个变量,范围在数字 1 到 6 之间,去渲染标题组件 h1~h6

如果使用template实现,非常的丑陋且冗余。

<h1 v-if="num==1">{{title}}</h1>
<h2 v-if="num==2">{{title}}</h2>
<h3 v-if="num==3">{{title}}</h3>
<h4 v-if="num==4">{{title}}</h4>
<h5 v-if="num==5">{{title}}</h5>
<h6 v-if="num==6">{{title}}</h6>

这个时候Vue2推荐了render函数:

Vue.component('anchored-heading', {
  render: function (h) {
    return h(
      'h' + this.level,   // 标签名称
      this.$slots.default // 子节点数组
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})

可以看到,使用render函数后代码量变得非常少,且十分优雅。

Vue3中,我们也可以使用render函数:

import { defineComponent, h } from 'vue'

export default defineComponent({
  props: {
    level: {
      type: Number,
      required: true
    }
  },
  setup(props, { slots }) {
    return () => {
      return h('h' + props.level, {}, slots.default())
    }
  }
})

但是如果是复杂的场景,h 函数写起来就显得非常繁琐,需要自己把所有的属性都转变成对象。并且组件嵌套的时候,对象也会变得非常复杂。

不过,因为 h 函数也是返回虚拟 DOM 的,所以有没有更方便的方式去写 h 函数呢?

答案是肯定的,这个方式就是 JSX

JSX

JSX 来源自 React 框架,下面这段代码就是 JSX 的语法,我们给变量 title 赋值了一个 h1 标签。

const element = <h1 id="app">Hello, Vue3!</h1>

这种在 JavaScript 里面写 HTML 的语法,就叫做 JSX,算是对 JavaScript 语法的一个扩展。

上面的代码直接在 JavaScript 环境中运行时,会报错。

JSX 的本质就是下面代码的语法糖,h 函数内部也是调用 createVnode 来返回虚拟 DOM。

const element = createVnode('h1',{id:"app"}, 'hello vue3')

JSXcreateVNode 函数的转化过程中,我们需要安装一个 JSX 插件。在项目的根目录下,打开命令行,执行下面的代码来安装插件:

npm install @vitejs/plugin-vue-jsx -D
// vite.config.js
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx';
export default defineConfig({
  plugins: [vue(),vueJsx()]
})

对于上面的例子,我们就可以这么来写了,首先创建于一个后缀是.jsx的文件:

setup(props, { slots }) {
   const tag = 'h'+props.level
   return () => <tag>{slots.default()}</tag>
}

下面来使用JSX对一个稍微复杂组件的改写:


import { defineComponent, ref } from 'vue'

export default defineComponent({
  setup(props) {
    let title = ref('')
    let todos = ref([{ title: "学习 Vue 3", done: true },{ title: "睡觉", done: false }]);
    function addTodo(){
        todos.value.push({
            title:title.value
        })
        title.value = ''
    }
    return () => <div>
        <input type="text" vModel={title.value} />
        <button onClick={addTodo}>click</button>
        <ul>
            {
                todos.value.length ? todos.value.map(todo=>{
                    return <li>{todo.title}</li>
                }): <li>no data</li>
            }
        </ul>
    </div>
  }
})

这与template模板有如下不同:

  1. 使用 defineComponent 的方式来定义组件;
  2. return了一个函数,该函数用来生成虚拟DOM;
  3. 使用 vModel 取代 v-model,并且使用单个大括号包裹的形式传入变量 title.value
  4. 使用 onClick 取代 @click
  5. 循环渲染清单的时候,使用.map 映射取代 v-for,使用三元表达式取代 v-if

通过这个例子,发现使用 JSX 的本质,还是在写 JavaScript

在组件库的设计中,有很多场景需要使用JSX,比如下面这个例子:

export const Timeline = (props)=>{
    const timeline = [
        <div class="start">8.21 开始自由职业</div>,
        <div class="online">10.18 专栏上线</div>
    ]
    if(props.reverse){
        timeline.reverse()
    }
    return <div>{timeline}</div>
}

如果你使用template模板实现上面这个简单的功能,会感觉是非常不优雅。

JSXTemplate的选择

template 的语法是固定的,如 v-ifv-for 等,这种固定格式的语法书写可以让 Vue 在编译层面就可以很方便地去做静态标记的优化

也就是,在页面更新重新执行 render时,代码里的纯静态的标签,如<div>123<div>,它没有引用响应式数据,那么这类静态标签在虚拟 DOM 计算的时候,会直接越过 Diff 过程。

当然,除了静态标记外,Vue3还利用了缓存机制来加快Diff过程。这也是 Vue 3 的虚拟 DOM 能够比 Vue 2 快的一个重要原因。

JSX 只是 h 函数的一个语法糖,本质就是 JavaScript,想实现条件渲染可以用 if else,也可以用三元表达式,还可以用任意合法的 JavaScript 语法。

也就是说,JSX 可以支持更动态的需求。而 template 则因为语法限制原因,不能够像 JSX 那样可以支持更动态的需求。这是 JSX 相比于 template 的一个优势。

JSX 相比于 template 还有一个优势,是可以在一个文件内返回多个组件,我们可以像下面的代码一样,在一个文件内返回 Button、Input 等多个组件。


export const Button = (props,{slots})=><button {...props}>slots.default()</button>
export const Input = (props)=><input {...props} />

templateJSX 这两者的选择问题上,只是选择框架时角度不同而已。我们实现业务需求的时候,也是优先使用 template,动态性要求较高的组件使用 JSX 实现,尽可能地利用 Vue 本身的性能优化。

总结

首先回顾了Vue2时代如何应对一些灵活性的场景,主要是手写render函数,但是对于一些复杂的组件,这种方式就显得力不从心了。

于是在Vue3中诞生了JSXJSX的概念来自于react,它可以使用JavaScript来书写HTML,这样就可以灵活的书写复杂组件了。

在最后编译时,JSX仍然是被编译为render函数,它只是方便了我们书写而已。

到底选择哪种模式开发,主要看使用的场景,一般业务组件都是使用template模板语法,这是尽可能利用Vue本身的性能优化,对于动态性较高的场景则可以使用JSX

转载自:https://juejin.cn/post/7241790368948617253
评论
请登录