介绍
JSX 是一种JavaScript 语法扩展,允许在 JS 代码中直接写类 HTML 标签,Vue 3 完全支持 JSX/TSX,是 Vue 模板语法的灵活替代品,适合复杂逻辑渲染、高阶组件、函数式组件场景。
概念
Vue + JSX 本质
用 JavaScript 的完整能力写 Vue 渲染函数,替代<template>模板,所有 Vue 响应式、指令、组件特性都能在 JSX 中使用。适用场景
- 复杂条件渲染、循环嵌套(模板写起来繁琐)
- 高阶组件、函数式组件
- 需要动态生成标签/属性的场景
- TS 类型支持更友好(TSX)
环境支持
- Vue 3 + Vite:默认支持
.jsx/.tsx,无需额外配置 - Vue CLI:已内置支持
- Vue 3 + Vite:默认支持
基础语法
1. 文件与结构
Vue 组件用 JSX 时,去掉 <template>,直接在 setup 或 render 中返回 JSX:
// 1. 基础 JSX 组件(Hello.jsx)
import { ref } from 'vue'
// 组件直接导出函数
export default () => {
const msg = ref('Hello Vue JSX')
// 直接返回 JSX
return <div>{msg.value}</div>
}
// 2. TSX 版本(Hello.tsx,带类型)
import { ref, defineComponent } from 'vue'
export default defineComponent({
setup() {
const msg = ref<string>('Hello TSX')
return () => <div>{msg}</div> // Vue 3 可自动解包 ref,不用 .value
}
})
2. 插值表达式
和模板一致,用 {} 包裹 JS 变量/表达式:
const name = 'Vue'
const count = ref(10)
return (
<div>
{/* 文本插值 */}
<p>你好,{name}</p>
{/* 表达式插值 */}
<p>计数:{count.value + 1}</p>
</div>
)
3. 标签与属性
- 单标签必须闭合:
<img />、<input /> - 属性用驼峰命名(class → className,for → htmlFor)
- 动态属性直接用
{},不用v-bind
const isDisabled = ref(false)
const bgClass = 'box'
return (
<div
className={bgClass} // class → className
id="app"
style={{ color: 'red', fontSize: '20px' }} // 样式用对象
disabled={isDisabled.value} // 动态属性
>
JSX 属性
</div>
)
Vue 核心特性在 JSX 中的写法
这是最常用的部分,直接对照模板语法学习:
1. 条件渲染(v-if / v-show)
JSX 中没有 v-if,用 JS 三元表达式/逻辑与运算符 替代:
const show = ref(true)
const type = ref(1)
return (
<div>
{/* 等价 v-show */}
<p style={{ display: show.value ? 'block' : 'none' }}>v-show</p>
{/* 等价 v-if / v-else */}
{show.value ? <p>显示</p> : <p>隐藏</p>}
{/* 多条件 */}
{type.value === 1 ? <p>类型1</p> : type.value === 2 ? <p>类型2</p> : <p>其他</p>}
</div>
)
2. 列表渲染(v-for)
用 JS map() 循环 替代 v-for,必须加 key:
const list = ref([{ id: 1, name: '苹果' }, { id: 2, name: '香蕉' }])
return (
<ul>
{list.value.map(item => (
<li key={item.id}>{item.name}</li> // key 必须写
))}
</ul>
)
3. 事件绑定(v-on / @)
用 on+驼峰事件名 替代 @:
const count = ref(0)
const add = () => count.value++
return (
<button onClick={add}>
计数:{count.value}
</button>
)
带参数/事件对象:
const handleClick = (e, id) => {
e.preventDefault()
console.log(id)
}
return <button onClick={(e) => handleClick(e, 123)}>点击</button>
4. 双向绑定(v-model)
- 基础用法:直接用
v-model={变量} - 自定义组件/指定属性:
v-model:propName={变量}
const text = ref('')
const visible = ref(false)
return (
<div>
<input v-model={text.value} />
<p>输入:{text.value}</p>
{/* 自定义组件 v-model */}
<Dialog v-model:visible={visible.value} />
</div>
)
5. 插槽(slot)
JSX 中插槽用子组件/对象传递,替代 template #插槽名:
默认插槽
// 父组件
import Child from './Child.jsx'
export default () => {
return (
<Child>
<p>这是默认插槽内容</p>
</Child>
)
}
// 子组件 Child.jsx
export default (_, { slots }) => {
// slots.default() 渲染默认插槽
return <div>{slots.default()}</div>
}
具名插槽 + 作用域插槽
// 父组件
export default () => {
return (
<Child>
{{
// 具名插槽 header
header: () => <h3>标题</h3>,
// 作用域插槽(子组件传数据)
default: (scope) => <p>子组件数据:{scope.msg}</p>
}}
</Child>
)
}
// 子组件
export default (_, { slots }) => {
return (
<div>
{slots.header?.()}
{slots.default({ msg: '我是子组件' })}
</div>
)
}
Props 与 Emits(TSX 推荐)
1. Props 声明
import { defineComponent } from 'vue'
export default defineComponent({
// 声明 props
props: {
title: {
type: String,
default: '默认标题'
},
count: Number
},
setup(props) {
// 直接使用 props
return () => (
<div>
<h2>{props.title}</h2>
<p>{props.count}</p>
</div>
)
}
})
2. 自定义事件(emits)
export default defineComponent({
emits: ['change'],
setup(_, { emit }) {
const handleChange = () => emit('change', '新值')
return () => <button onClick={handleChange}>触发事件</button>
}
})
高级用法
1. 动态组件
用 resolveComponent 配合 h 函数:
import { resolveComponent, h } from 'vue'
const ComName = 'Button'
const Button = resolveComponent(ComName)
return <Button type="primary">动态组件</Button>
2. Teleport(传送门)
import { Teleport } from 'vue'
return (
<Teleport to="body">
<div className="modal">模态框</div>
</Teleport>
)
3. 函数式组件(无状态、高性能)
// 无响应式,纯接收 props 渲染
export default (props) => {
return <div>{props.text}</div>
}
模板 vs JSX 对比
| 特性 | 模板语法 | JSX 语法 |
|---|---|---|
| 学习成本 | 低,接近 HTML | 中,需要 JS 基础 |
| 复杂逻辑 | 繁琐,指令嵌套 | 灵活,原生 JS 逻辑 |
| TS 支持 | 一般(需额外配置) | 完美,TSX 原生支持 |
| 编译优化 | Vue 自动优化 | 需手动优化 |
| 适用场景 | 通用页面、简单组件 | 复杂组件、高阶组件 |
常见坑点
- class / for 改名
class→classNamefor→htmlFor
- 样式必须写对象
- 错误:
style="color:red" - 正确:
style={{color:'red'}}
- 错误:
- 循环必须加 key
- map 循环必须绑定唯一 key,否则警告
- 指令大部分废弃
- 无
v-if/v-for,用 JS 原生语法替代
- 无
- ref 自动解包
- JSX 中直接写
{count},Vue 会自动解包,不用.value(setup 内返回 JSX)
- JSX 中直接写
总结
- Vue JSX 是模板的增强方案,用 JS 原生能力实现复杂渲染;
- 核心替换:
v-if→三元、v-for→map、@click→onClick; - 适合复杂组件/TS 项目,简单页面优先用模板;
- Vue 3 对 JSX 支持极友好,Vite 开箱即用,无额外配置成本。