vue3 中如何使用 JSX?

Daotin 于 2022-10-20 发布 编辑

在绝大多数情况下,Vue 推荐使用模板<template>语法来创建应用。

在 Vue 3 的项目开发中,template 是 Vue 3 默认的写法。虽然 template 长得很像 HTML,但 Vue 其实会把 template 解析为 render 函数,之后,组件运行的时候通过 render 函数去返回虚拟 DOM。

然而在某些使用场景下,我们真的需要用到 JavaScript 完全的编程能力。这时渲染函数就派上用场了。

h 函数

Vue 提供了一个 h() 函数用于创建 vnodes 虚拟 dom。如下示例:

import { h } from 'vue'

const vnode = h(
  'div', // type
  { id: 'foo', class: 'bar' }, // props
  [
    /* children */
  ]
)

在组件中如何使用呢?

之前当组合式 API 与模板一起使用时,setup() 钩子的返回值是用于暴露数据给模板。但是当使用h()的时候,setup() 钩子返回的是渲染函数

import { ref, h } from 'vue'

export default {
  props: {
    /* ... */
  },
  setup(props) {
    const count = ref(1)

    // 返回渲染函数
    return () => h('div', props.msg + count.value)
  }
}

手写的 h 函数,可以处理动态性更高的场景。但是如果是复杂的场景,h 函数写起来就显得非常繁琐,需要自己把所有的属性都转变成对象。并且组件嵌套的时候,对象也会变得非常复杂。不过,因为 h 函数也是返回虚拟 DOM 的,所以有没有更方便的方式去写 h 函数呢?答案是肯定的,这个方式就是 JSX。

JSX

JSX 是 JavaScript 的一个类似 XML 的扩展,有了它,我们可以用简单方式来创建 vnodes:

const vnode = <div id="app">hello</div>

这种在 JavaScript 里面写 HTML 的语法,就叫做 JSX,算是对 JavaScript 语法的一个扩展。上面的代码直接在 JavaScript 环境中运行时,会报错。JSX 的本质就是下面代码的语法糖。

const vnode = createVnode('div',{id:"app"}, 'hello')

其实 h 函数内部也是调用 createVnode 来返回虚拟 DOM。

那么,如何在 Vue 项目中使用 JSX 呢?

默认的情况下,vue3+vite 的项目不支持 jsx,如果想支持 jsx,需要安装插件@vitejs/plugin-vue-jsx

安装

npm i @vitejs/plugin-vue-jsx -D

在 vite.config.js 中进行配置:

import vueJsx from "@vitejs/plugin-vue-jsx"; // 配置vue使用jsx

export default defineConfig({
  plugins: [
    vue(),
    vueJsx()
  ],
});

然后就可以在 Vue 组件中使用了:

// render.vue
<script lang="jsx">
import { ref } from "vue";
export default {
  setup() {
    const count = ref(100);
    return () => <div>count is: {count.value}</div>;
  },
};
</script>

注意:script 中 lang 要改成 jsx。

或者是一个 jsx 文件:

// render.jsx
import { defineComponent } from 'vue'

export default defineComponent({
  setup() {
    return () => <div>jsx文件</div>
  }
})

Template vs JSX

我们该怎么选择 JSX 和 template 呢?

template 优势:template 的语法是固定的,只有 v-if、v-for 等等语法。我们按照这种固定格式的语法书写,这样 Vue3 在编译层面就可以很方便地去做静态标记的优化,减少 Diff 过程。比如静态提升,类型标记,树结构打平等来提高虚拟 DOM 运行时性能。这也是 Vue 3 的虚拟 DOM 能够比 Vue 2 快的一个重要原因。

参考:Vue 渲染机制

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

JSX 相比于 template 还有一个优势,是可以在一个文件内返回多个组件。

那如何选择呢?

在实现业务需求的时候,优先使用 template,尽可能地利用 Vue 本身的性能优化。而对于动态性要求较高的组件可以使用 JSX 来实现。(比如后面,我会用 JSX 来实现动态表单生成器)

总结

首先,我们在项目中通常使用 template 模板来创建应用,template 模板会在构建阶段被编译成 render 渲染函数。渲染函数就是用来返回虚拟 DOM 的函数。

那么我们其实可以跳过这个步骤,直接使用 h 函数来生成虚拟 DOM。

h 函数的内部执行的其实是 createVNode 函数来生成虚拟 DOM 的,但是由于 h 函数比较难写,所以我们使用 JSX 来更加方便快捷的书写。JSX 的内部其实也是使用的 createVNode 函数。

JSX 在 JavaScript 中是不能直接执行的,在 vite 项目中,我们需要安装插件@vitejs/plugin-vue-jsx

,并进行相应配置才可以。

最后对比了 template 和 JSX 的优劣势。template 的优势在于语法固定,容易书写,并且在编译层面 Vue3 对 template 的编译做了很多优化。而 JSX 胜在灵活性,在某些动态性要求较高的情况下,JSX 成了标配。

所谓,术业有专攻,template 和 JSX 没有谁比谁更厉害的存在,在不同场景下各有优劣,用好了都是编程利器。