Vue 3 中使用 JSX 的正确姿势与实战指南
本文深入解析 Vue 3 中 JSX 的使用方式,从虚拟 DOM 原理到渲染函数,详细讲解 JSX 在 Vue 中的两种实现路径:直接修改后缀名与 setup 中返回 render。内容涵盖 class、style、props、事件及插槽的绑定技巧,并针对 TypeScript 类型报错提供解决方案,帮助开发者高效掌握 Vue 组件的 JSX 编写规范。
前言
趁着空闲时间,我决定深入探索 H5 页面开发。长期以来,我主要使用 Vant 框架,但出于对源码实现的好奇,我从 Git 拉取了 Vant 的完整源码。令人惊讶的是,其内部组件大量使用了 JSX 语法。这促使我开始深入研究在 Vue 框架中正确使用 JSX 的方法。
虚拟DOM
什么是虚拟DOM
在深入 JSX 之前,有必要先理解虚拟 DOM 的概念。Vue 和 React 等主流框架均在底层使用了虚拟 DOM。其核心动机在于,直接通过 JavaScript 操作真实 DOM 的计算成本极高。尽管 JS 引擎执行速度快,但查找并更新真实 DOM 节点的过程非常耗时。为了优化性能,Vue 等框架采用 JavaScript 对象来模拟 DOM 结构。通过修改这些 JS 对象,框架可以进行批量处理,最终一次性更新真实 DOM。因此,虚拟 DOM 本质上就是一个描述 UI 结构的 JavaScript 对象。
虚拟DOM的优点
- 从操作真实 DOM 转变为操作虚拟 DOM,显著降低了 DOM 查找和更新的成本。
- 借助 diff 算法进行比对,能够更快速地定位数据变化,从而精准更新 DOM。
- 提供更优化的 UI 更新机制。
- 抽象了渲染过程,赋予了跨平台开发的能力。例如,Vue 3 中的 createRenderer API 便利用了这一特性。
渲染函数是什么
渲染函数的主要作用是生成虚拟 DOM。在 Vue 单文件组件中,我们通常编写模板语法,这些模板在底层会被编译成渲染函数。
Vue 官方推荐在绝大多数场景下使用模板来构建 HTML。然而,在某些需要完全发挥 JavaScript 编程能力的场景中,使用渲染函数是更好的选择,因为它比模板更接近编译器的输出。
当遇到以下场景时,虽然模板写法也能实现预期效果,但代码会显得冗长。例如,我们需要为不同级别的标题重复书写相同的结构。此外,当需要添加锚点元素时,必须在每个 v-if 或 v-else-if 分支中再次重复该逻辑。
const { createApp } = Vue
const app = createApp({})
app.component('anchored-heading', {
template: `
<h1 v-if="level === 1">
<slot></slot>
</h1>
<h2 v-else-if="level === 2">
<slot></slot>
</h2>
<h3 v-else-if="level === 3">
<slot></slot>
</h3>
<h4 v-else-if="level === 4">
<slot></slot>
</h4>
<h5 v-else-if="level === 5">
<slot></slot>
</h5>
<h6 v-else-if="level === 6">
<slot></slot>
</h6>
`,
props: {
level: {
type: Number,
required: true
}
}
})
尽管模板在大多数组件中表现优异,但在上述特定场景下显然不再适用。接下来,我们将尝试使用 render 函数重写上述示例。
const { createApp, h } = Vue
const app = createApp({})
app.component('anchored-heading', {
render() {
return h(
'h' + this.level, // tag name
{}, // props/attributes
this.$slots.default() // array of children
)
},
props: {
level: {
type: Number,
required: true
}
}
})
jsx
直接编写渲染函数可能会让人感到繁琐。是否有更接近模板语法的写法呢?Vue 提供了一个名为 babel-plugin-jsx 的 Babel 插件,使得 Vue 能够支持 JSX 语法。
在本例中,我使用 Vue CLI 创建了一个基于 Vue 3 和 TypeScript 的项目。该脚手架默认集成了 JSX 和 TypeScript 的相关依赖。
在vue3中编写jsx的两种方式
直接将文件后缀名从.vue改为.tsx或.jsx。
在 Vue 3 中,可以直接使用 render 选项来编写组件。
import { defineComponent } from "vue";
export default defineComponent({
name: "Jsx",
render() {
return <div>我是一个div</div>;
},
});
或者,也可以在 setup 函数中返回渲染函数。
import { defineComponent } from "vue";
export default defineComponent({
name: "Jsx",
setup() {
return () => <div>我是div</div>;
},
});
这两种方式均可使用,具体取决于个人习惯。需要注意的是,在 setup 中无法直接访问 this,但在 render 选项中可以通过 this 访问当前的 Vue 实例。
用法
class 绑定与 React 的 JSX 绑定有所区别。在 React 中使用 className,而在 Vue 中直接使用 class。
setup() {
return () => <div class="test">我是div</div>;
},
style 绑定
setup() {
return () => <div style={{ color: "red" }}>我是div</div>;
},
props 绑定
// 父组件
setup() {
return () => (
<div style={{ color: "red" }}>
<span>我是父组件</span>
<Mycom msg={"我是父组件传的值"} />
</div>
);
// 子组件,setup的第一个参数,可以获取props里的值
setup(props) {
return () => <div>我是子组件{props.msg}</div>;
},
事件绑定
setup() {
function eventClick() {
console.log("点击");
}
return () => <button onClick={eventClick}>按钮</button>;
},
组件自定义事件
// 子组件
import { defineComponent } from "vue";
export default defineComponent({
name: "Mycom",
emits: ["event"],
setup(props, { emit }) {
function sendData() {
emit("event", "子组件传递的数据");
}
return () => (
<div>
<span>自定义事件</span>
<button onClick={sendData}>传递数据</button>
</div>
);
},
});
// 父组件
// @ts-nocheck
// 这样写在jsx中没问题,但是在tsx中会报ts类型错误,所以我在上面忽略了当前文件ts监测@ts-nocheck
import { defineComponent } from "vue";
import Mycom from "./mycom";
export default defineComponent({
name: "Jsx",
setup() {
function getSon(msg: string) {
console.log(msg);
}
return () => (
<div>
<Mycom onEvent={getSon} />
</div>
);
},
});
如果希望解决 TypeScript 的类型报错,可以采用以下处理方式。
setup() {
function getSon(msg: string) {
console.log(msg);
}
return () => (
<div>
<Mycom {...{ onEvent: getSon }} />
</div>
);
},
插槽
// 父组件
setup() {
const slots = {
test: () => <div>具名插槽</div>,
default: () => <div>默认插槽</div>,
};
return () => (
<div>
<Mycom v-slots={slots}></Mycom>
</div>
);
},
setup(props, { slots }) {
// 子组件
return () => (
<div>
<span>插槽</span>
{slots.default?.()}
{slots.test?.()}
</div>
);
},
关于指令,v-if、v-for 等指令在 JSX 中无法直接使用。JSX 仅支持 v-model 和 v-show 指令。
setup() {
const inputData = ref("");
return () => (
<div>
<span v-show={true}>显示</span>
<span v-show={false}>隐藏</span>
<input type="text" v-model={inputData.value} />
<span>{inputData.value}</span>
</div>
);
},
最后
话不多说,我现在打开 Vant 源码,准备开始阅读我的第一个组件源码,路径为 src => button => button.tsx。
到此这篇关于 Vue 中正确使用 JSX 的文章就介绍到这了,更多相关 Vue 中使用 JSX 的内容请搜索本站以前的文章或继续浏览下面的相关文章。希望大家以后多多支持本站!
参考
- vue渲染函数
- vuejsx文档
- issues
React 与 Vue 3 核心差异解析:技术选型参考
2026-11-05 13:25:04
利用JS三目运算符简化React和Vue组件中的条件渲染
2026-11-05 12:50:09
Vite 与 Webpack 在 React 项目中的配置差异
2026-11-05 10:13:11
React Hooks 实战:useState 与 useEffect 使用规范
2026-11-04 18:46:10
基于 UmiJS 与 Ant Design Pro 搭建企业级中后台系统
2026-11-03 16:42:02








