vue jsx/tsx 的本质
不知道你有没有想过,为什么 jsx
支持,需要安装包 @vitejs/plugin-vue-jsx
实际上,所有的 jsx/tsx
都会经过 babel
编译,变成最后的 js
的
其中 vue
编写了 @vue/babel-plugin-jsx 编译插件,作为自定义的编译规则
什么是 JSX 文件
JSX 本质上不是浏览器或 JavaScript 原生支持的语法,它是一种语法糖,需要通过 Babel 转换成 JavaScript 函数调用才能执行。
例如在 React 中:
<div className="greeting">Hello, JSX</div>
会被 Babel 转换为:
import { jsx as _jsx } from 'react/jsx-runtime'
/* #__PURE__ */_jsx('div', {
className: 'greeting',
children: 'Hello, JSX'
})
在 Vue 中,JSX 也是类似地转换成函数调用,只是调用的是 Vue 的渲染函数 API。
为什么 Vue 需要专门的 Babel 插件?
Vue 的 JSX 和 React 的 JSX 看起来很像,但底层实现完全不同。Vue 使用的是 createVNode()
来描述虚拟 DOM。
因此,如果你直接使用标准的 Babel JSX 插件(如 @babel/plugin-transform-react-jsx
),会默认把 JSX 编译为 React.createElement
/ jsx
,这就不兼容 Vue
了。
为了解决这个问题,Vue 官方编写了自己的 Babel 插件:@vue/babel-plugin-jsx
@vue/babel-plugin-jsx 做了什么?
这个插件做了非常多底层定制工作,核心功能包括:
1. 将 JSX 编译为 Vue 的 createVNode()
调用
<div id="foo">bar</div>
会变成:
_createVNode('div', {
id: 'foo'
}, [_createTextVNode('bar')])
而 createVNode
默认是从当前作用域中引入的 Vue 的 createVNode()
函数。
2. 支持 Vue 特有语法
v-model
转换v-show
/v-html
等指令- 插槽(Slots)的识别与转换
例如:
<>
<input v-model={modelValue} />
<Comp v-model={modelValue}></Comp>
</>
会被编译为:
_createVNode(_Fragment, null, [
_withDirectives(_createVNode('input', {
'onUpdate:modelValue': $event => modelValue = $event
}, null), [[_vModelText, modelValue]]),
_createVNode(_resolveComponent('Comp'), {
'modelValue': modelValue,
'onUpdate:modelValue': $event => modelValue = $event
}, null)
])
3. 事件绑定语法
<MyComponent onClick={handler} />
_createVNode(_resolveComponent('MyComponent'), {
onClick: handler
}, null)
4. 自动注入作用域变量(如 ctx.slots
)
Vue 组件渲染函数的上下文是通过 setup()
中的返回值或 render()
中传入的 ctx
提供的。插件会自动帮你处理这些作用域引用。
为什么使用 Vite 时还需要安装 @vitejs/plugin-vue-jsx
?
Vite 本身并不自带对 Vue JSX 的支持,它是通过这个插件来集成 Babel 的编译工作(实际上是调用上面的 @vue/babel-plugin-jsx
插件)。
这个 Vite 插件做了两件事:
- 注册
@vue/babel-plugin-jsx
供 Babel 使用 - 确保 JSX 文件正确走过 Vue 的构建流程(热更新、模块分析等)
总结:Vue JSX 的本质
- JSX 是一种语法糖,需要被 Babel 编译
- Vue 的
@vue/babel-plugin-jsx
负责将 JSX 转换为 Vue 的createVNode()
调用,并支持 Vue 特有语法 - 使用 JSX 并不意味着和 React 有任何运行时耦合,它只是 Vue 渲染函数的一种书写形式
- 在 Vite 中使用 Vue JSX,需要额外安装
@vitejs/plugin-vue-jsx
来接入 Babel 插件系统