如何在 Vue 项目的 template 中使用 JSX

相信写过 JSX 的人再去写 Vue 中的 <template>,很多人会觉得束缚太多了,语法不够灵活。我也深有此感,所以琢磨了在 <template> 中使用 JSX 的一些方式,特此记录与大家分享。

两种写法的对比

先来看下常规的 <template> 的写法:

html 复制代码
<script setup lang="tsx">
import { reactive } from 'vue'
const state = reactive({
  status: null as '成功' | '失败' | '未知' | null,
  message: null as null | string,
})
MockAPI().then((data) => { /** 调用接口,填充 state 数据 */ })
</script>

<template>
  <div role="demo">
    <div v-if="!state.status">状态获取中...</div>
    <div
      v-else
      :class="{
        'text-success': state.status === '成功',
        'text-error': state.status === '失败',
        'text-warning': state.status === '未知',
      }"
    >
      <div role="content" v-if="!state.message">
        <span>状态:{{ state.status }}</span>
        <a-icon v-if="['成功', '失败'].includes(state.status)" :type="state.status === '成功' ? 'check' : 'close'" />
      </div>
      <!-- 失败且有失败原因时,展示失败原因 -->
      <a-tooltip v-else :title="state.message">
        <div role="content">
          <span>状态:{{ state.status }}</span>
          <a-icon v-if="['成功', '失败'].includes(state.status)" :type="state.status === '成功' ? 'check' : 'close'" />
        </div>
      </a-tooltip>
    </div>
  </div>
</template>

这是常见的在 <template> 中写所有的元素,可以对比下使用 <template> + JSX 的方式:

html 复制代码
<script setup lang="tsx">
// ...
const Node = {
  render() {
    if (!state.status) return <div v-if="!state.status">状态获取中...</div>

	  const clsMap = {
	    成功: 'text-success',
	    失败: 'text-error',
	    未知: 'text-warning',
	  }
    const showIcon = ['成功', '失败'].includes(state.status)
    const content = (
      <div role="content">
        <span>状态:{state.status}</span>
        {showIcon && <a-icon type={state.status === '成功' ? 'check' : 'close'} />}
      </div>
    )
    return (
      <div class={clsMap[state.status]}>
        {/* 使用 JSX,content 可以重用 */}
        {!state.message ? content : <a-tooltip title={state.message}>{content}</a-tooltip>}
      </div>
    )
  },
}
</script>

<template>
  <div role="demo">
    <component :is="Node" />
  </div>
</template>

通过对比,可以看到使用 <template> + JSX 的方式有如下优点:

  • 类似 hooks,将高度相关的函数、变量与元素耦合在一起,代码更简洁更好维护
  • 使用 JSX 实现更灵活的逻辑判断
  • 为数据命名有意义的变量名,如上面的 showIcon 和 clsMap,增加代码可读性
  • 借助 JS 编程减少重复代码,如上面提取的 div[role="content"] 元素

上面这种实现方式的原理很简单, <componet> 是 Vue 内置的组件, is 属性接收一个组件的定义, const Node = { render() {} } 其实就是一个组件内定义的选项式组件,使用 const Node = () => {} 函数式组件也是可以的。

自定义 组件实现

除此之外,自定义一个全局的 <jsx> 组件也是可以实现的:

tsx 复制代码
// Jsx.jsx
const Jsx = defineComponent({
  name: 'Jsx',
  props: {
    node: {
      // 这里的 Object 实际上特指 VNode 类型
      type: [Object, String, Number],
    },
    // Vue2 中的组件必须用实际的标签包裹
    tag: {
      type: String,
      default: 'div',
    },
  },
  setup(props) {
    // 注意:props.node 允许为空(null、undefined),这时还是会渲染出一个内容为空的节点
    return () => h(props.tag, [props.node as any])
  },
})

// vue 项目入口文件 main.ts
import Vue from 'vue'
Vue.component('Jsx', Jsx)

使用方式如下:

html 复制代码
<script setup lang="tsx">
function renderNode() {
  return <div>JSX 内容...</div>
}
</script>

<template>
	<jsx :node="renderNode()" />
</template>

不适用的场景

当然了,<template> + JSX 也并不适用于所有的场景。比如下面这几个场景

1. 逻辑简单

逻辑简单时,使用 <template> 即可,没必要为了用 JSX 而用 JSX。

2. 逻辑过于复杂

当逻辑过于复杂时,建议提取为一个单独的组件。

3. 想要更好的 Vue 编译优化、热更新

由于 <template> 是静态的,而 JSX 是动态的,所以 Vue 对前者的编译优化会更好些。此外经过我的实践, JSX 在很多时候热更新会状态丢失,稍稍影响到了开发体验。

相关推荐
用户461169657285 分钟前
网站 HTTPS 不稳定?证书排查指南
前端
n876516 分钟前
Playwright Java WEB UI 自动化实现框架与逻辑
java·前端·ui
keyipatience18 分钟前
TypeScript学习(1)
前端·javascript·学习·typescript·html·css3
xifangge20251 小时前
基于 JavaScript 实现 CNC 数控切削参数与螺纹公差纯前端核算引擎
开发语言·前端·javascript
wangruofeng1 小时前
2026 Web 技术选型指南,AI 时代如何选择适合项目的技术栈
前端·aigc·ai编程
honkun61 小时前
vue 表格组件 vxe-table 修改滚动条样式,修改滚动条样式,统一宽度、颜色与自动隐藏
前端·javascript·vue.js
爱勇宝2 小时前
研究数字孪生的这半年,做了个 Demo,也有了一些新想法
前端·程序员
不可能片场2 小时前
工程实践 一个缓存把选片从十分钟压到一分钟
前端·electron
前端小万2 小时前
探索副业五个月,最终以放弃告终
前端·后端·面试
FRing032 小时前
JavaScript入门到精通二十四(正则表达式、元字符、修饰符)
开发语言·前端·javascript·正则表达式