JSX 详细说明(VUE方向)

介绍

JSX 是一种JavaScript 语法扩展,允许在 JS 代码中直接写类 HTML 标签,Vue 3 完全支持 JSX/TSX,是 Vue 模板语法的灵活替代品,适合复杂逻辑渲染、高阶组件、函数式组件场景。

概念

  1. Vue + JSX 本质
    用 JavaScript 的完整能力写 Vue 渲染函数,替代 <template> 模板,所有 Vue 响应式、指令、组件特性都能在 JSX 中使用。

  2. 适用场景

    • 复杂条件渲染、循环嵌套(模板写起来繁琐)
    • 高阶组件、函数式组件
    • 需要动态生成标签/属性的场景
    • TS 类型支持更友好(TSX)
  3. 环境支持

    • Vue 3 + Vite:默认支持 .jsx/.tsx,无需额外配置
    • Vue CLI:已内置支持

基础语法

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 自动优化 需手动优化
适用场景 通用页面、简单组件 复杂组件、高阶组件

常见坑点

  1. class / for 改名
    • class → className
    • for → htmlFor
  2. 样式必须写对象
    • 错误:style="color:red"
    • 正确:style={{color:'red'}}
  3. 循环必须加 key
    • map 循环必须绑定唯一 key,否则警告
  4. 指令大部分废弃
    • 无 v-if/v-for,用 JS 原生语法替代
  5. ref 自动解包
    • JSX 中直接写 {count},Vue 会自动解包,不用 .value(setup 内返回 JSX)

总结

  1. Vue JSX 是模板的增强方案,用 JS 原生能力实现复杂渲染;
  2. 核心替换:v-if→三元、v-for→map、@click→onClick;
  3. 适合复杂组件/TS 项目,简单页面优先用模板;
  4. Vue 3 对 JSX 支持极友好,Vite 开箱即用,无额外配置成本。

原文出处:/show_1GW3L0xTEcPP.html