JSX与TSX核心原理详解:从React到Vue 3的完整实践指南
这两年前端圈有一个很有意思的现象很多人是从 Vue 的模板语法开始写页面后来遇到 React 项目看到className、onClick、花括号里塞 JS 表达式第一反应是“这什么玩意HTML 和 JS 混在一起还能这么玩”。等到上手写 TSX又被泛型、类型推断、组件 props 的定义方式折磨得够呛。反过来Vue 3 全面拥抱 TS 之后越来越多同学开始在 invest setup 里尝试 JSX 写法尤其是做组件库、复杂渲染逻辑或动态组件场景模板有时候真的不够用。这篇内容我打算把 JSX 和 TSX 整个链路讲透JSX 到底是个什么产物、编译之后长什么样、TSX 在上面加了哪些约束以及最近热度很高的“Vue 3 中使用 JSX/TSX”应该怎么落地。无论你是 React 新手、Vue 老手还是全栈工程师这篇文章都能帮你少踩几个坑。1. 从模板字符串到 JSXUI 描述方式的演进1.1 早年怎么写页面模板字符串和模板引擎在 React 还没普及的年代前端渲染页面最朴素的方式就是字符串拼接。比如 jQuery 时代要渲染一个列表通常是这样const list [ { id: 1, name: 张三 }, { id: 2, name: 李四 }, ]; let html ul; for (const item of list) { html li>const element h1 classNametitleHello, {name}/h1;经过编译后在 React 17 之前的旧版转换方式下它等价于const element React.createElement( h1, { className: title }, Hello, , name );在 React 17 之后新的 JSX 转换机制则变成import { jsx as _jsx } from react/jsx-runtime; const element _jsx(h1, { className: title, children: [Hello, , name] });看到没有h1不是特殊语法它最终就是一个函数调用的第一个参数classNametitle就是传给函数的一个对象属性{name}就是函数的另一个参数。这意味着你完全可以使用 JavaScript 的变量、函数、三元表达式、数组方法去构造视图不再需要模板引擎去定义“if 指令”“for 指令”。提示JSX 的表达式必须放在花括号{}中。{name}里的 name 会被求值而不会原样输出字符串。1.3 JSX 为什么用 className 而不是 class这个问题几乎每个 JSX 新手都会问为什么 HTML 里写div classboxJSX 里要写div classNamebox原因很简单class在 JavaScript 里是保留字用于定义类。如果 JSX 里直接用class编译器解析时就会有歧义。同理label foremail在 JSX 中要写成label htmlForemail。所有 HTML 属性名在 JSX 中都要遵循驼峰命名法tabindex变成tabIndexmaxlength变成maxLengthonclick变成onClick。本质上是因为 JSX 属性最终会成为 JavaScript 对象的 key而对象 key 使用驼峰命名是工程上的共识。2. JSX 的编译机制与核心语法2.1 谁来把 JSX 编译成 JavaScriptJSX 不是浏览器原生支持的语法必须经过编译。主流方式有三种Babel搭配babel/preset-react或babel/plugin-transform-react-jsx这是最传统的方式React 项目默认使用。TypeScript 编译器在tsconfig.json中设置jsx: react-jsxTS 编译器可以直接把.tsx文件中的 JSX 转换掉。esbuild / SWC新一代构建工具编译速度快Vite 底层就用的 esbuildSWC 则是 Next.js 默认编译器。以 Vite React 项目为例tsconfig.json里常见的配置是{ compilerOptions: { jsx: react-jsx, jsxImportSource: react } }jsx: react-jsx表示使用 React 17 之后的自动运行时转换编译器会自动从react/jsx-runtime导入jsx函数你不需要在每个文件顶部import React from react。2.2 条件渲染JSX 的“表达式即一切”模板引擎通常提供专门的指令来做条件渲染JSX 则直接利用 JavaScript 的逻辑表达式。常见写法有{isLoggedIn ? Dashboard / : LoginForm /}{unreadCount 0 span{unreadCount} 条未读/span}{ status success ? SuccessTip / : status error ? ErrorTip / : LoadingTip / }这里要注意一个细节表达式如果左侧是 0 或空字符串会因为 JavaScript 的 falsy 特性而渲染出0或空字符串本身。比如{count Span /}当 count 为 0 时页面会显示一个“0”。所以更稳妥的写法是{count 0 Span /}或者把值转成布尔值{!!count Span /}。2.3 列表渲染map 和 key 的规则列表渲染在 JSX 里就是调用数组的map方法ul {items.map((item) ( li key{item.id}{item.name}/li ))} /ulkey是 JSX 列表渲染中最容易出问题的点。React 和 Vue 都依赖 key 来高效复用节点key 必须稳定、唯一。很多人图省事直接用数组下标当 key这在列表项顺序不变时看起来没问题但一旦发生插入、删除或排序操作会导致组件状态错乱。比如下面这个例子列表前面插了一条数据子组件内部有input时输入框的内容会对应到错误的行{items.map((item, index) ( Row key{index} data{item} / ))}注意只要列表可能发生增删、排序就不要用 index 作为 key。优先使用业务 id如果没有唯一 id再考虑结合字段生成的复合 key。2.4 为什么说 JSX 是完整 JavaScript 表达力的体现模板语法设计的出发点是“限制”因为模板要尽量简单、声明式开发者不能在模板里写复杂逻辑。JSX 的出发点是“自由”它允许你在视图描述里直接调用函数、声明变量、做复杂运算。{ visibleList .filter((item) item.status active) .sort((a, b) b.updatedAt - a.updatedAt) .slice(0, 10) .map((item) ( Card key{item.id} title{item.title} onClick{() handleOpen(item.id)} / )); }这段逻辑如果放在 Vue 模板里就需要在computed里写一个计算属性然后在模板中引用它。两种方式都能实现但 JSX 让“内联/临时计算”这件事变得非常顺手尤其适合数据转换链路短、且只存在于渲染上下文的场景。3. TSX给 JSX 套上类型安全的外壳3.1 TSX 和 JSX 的关系TSX 严格意义上来说是 TypeScript 处理 JSX 语法时的专用文件扩展名。.tsx文件既包含 JSX又包含 TypeScript 类型标注。编译器处理.tsx文件时会先进行 JSX 语法转换再做类型检查。关键在于TSX 不是另一门 UI 语言它是在 JSX 之上的一层类型约束系统。类型标注会在编译后被擦除运行时和 JSX 没有区别。所以“JSX 和 TSX 详解”这个话题本质上是两件事JSX 解决的是“怎么写 UI”TSX 解决的是“怎么写 UI 才能让编译器帮忙防呆”。3.2 组件 props 的类型定义以 React 为例定义一个带类型的函数组件interface ButtonProps { label: string; variant?: primary | secondary; disabled?: boolean; onClick: () void; } const Button ({ label, variant primary, disabled false, onClick }: ButtonProps) { return ( button className{btn btn-${variant}} disabled{disabled} onClick{onClick} {label} /button ); };这里的ButtonProps会在你使用该组件时提供完整的语法提示。传错类型、漏传必传参数、传了不存在的属性编译器会直接报错。这是 TSX 相比 JSX 最大的红利可维护性。在大型项目里几十上百个组件全靠 JS 注释和自觉去约束 props 是不可想象的。3.3 泛型组件让类型跟随数据流动TSX 的进阶用法是泛型组件。假设我们做一个通用列表组件interface ListPropsT { items: T[]; renderItem: (item: T) React.ReactNode; } function ListT({ items, renderItem }: ListPropsT) { return {items.map(renderItem)}/; }使用时类型会自动跟随传入的items推断interface User { id: number; name: string; } List items{users} renderItem{(user) span{user.name}/span} /这里renderItem的参数user会被自动推断为User类型而不是any。在.tsx文件中写泛型箭头函数时注意要写成T,的形式多一个逗号让解析器知道这是泛型而不是 JSX 标签const getRenderValue T,(item: T) ${item};3.4 事件对象的类型冷门但实用的细节在 TSX 中事件处理函数的参数类型经常被人忽略。React 中 onClick 的事件类型是React.MouseEventHTMLButtonElement如果不做标注鼠标事件对象上的currentTarget、clientX等属性就无法获得正确提示。const handleClick (event: React.MouseEventHTMLButtonElement) { console.log(event.clientX, event.clientY); }; button onClick{handleClick}点击/button常见的事件类型还有React.ChangeEventHTMLInputElement输入框变化、React.FormEventHTMLFormElement表单提交、React.KeyboardEventHTMLInputElement键盘事件。搞不清事件类型时可以直接把鼠标悬浮在 JSX 的事件属性上IDE 会给出推断类型直接复制即可。4. Vue 3 中使用 JSX/TSX模板之外的另一种选择4.1 为什么 Vue 也要拥抱 JSX很多人以为 JSX 是 React 专属实际上 Vue 3 提供了完整的 JSX 支持。Vue 官方给出的解释是模板在多数场景下足够声明式和高效但遇到复杂动态渲染、函数式组件、以及需要在 render 函数中编写大量分支逻辑时模板的指令系统会显得笨重。举一个现实的例子状态驱动的动态组件。用模板写通常要包一层component :is...配合一大堆v-if分支用 JSX 写直接就是函数调用和条件表达式的组合逻辑一目了然。另外团队中如果同时有 React 和 Vue 项目JSX 写法可以降低技术栈切换的心智成本让经验的迁移更平滑。4.2 环境配置Vite 项目接入 JSXVue 3 使用 JSX 不需要引入 React它用的是vitejs/plugin-vue-jsx插件。安装并配置npm install vitejs/plugin-vue-jsx -D然后在vite.config.ts中import { defineConfig } from vite; import vue from vitejs/plugin-vue; import vueJsx from vitejs/plugin-vue-jsx; export default defineConfig({ plugins: [vue(), vueJsx()], });这样.tsx文件就能在 Vue 项目里直接写了。组件的默认导出方式推荐使用defineComponentimport { defineComponent, ref } from vue; export default defineComponent({ name: Counter, setup() { const count ref(0); const increment () count.value; return () ( div classcounter span{count.value}/span button onClick{increment}1/button /div ); }, });4.3 v-model、插槽、指令在 JSX 中怎么映射Vue 模板中的指令在 JSX 中有对应的写法但需要显式展开。以v-model为例模板里是input v-modelvalue /JSX 插件提供了语法糖可以直接写成input v-model{value} /如果你不想依赖语法糖也可以手动展开为“值 更新事件”的组合input modelValue{value} onUpdate:modelValue{(newVal) (value newVal)} /插槽在 JSX 中通过v-slots属性实现Child v-slots{{ default: () span默认插槽内容/span, header: (props) div{props.title}/div, }} /v-if在 JSX 里直接用或三元表达式替代v-for用map替代v-show则可以用style{{ display: show ? : none }}或直接调用v-show指令Vue 3 插件支持。4.4 Vue 模板和 JSX 该怎么选我的建议我们可以从几个维度对比一下维度Vue 模板JSX条件渲染v-if、v-show 语义化强三元、完全 JS 表达式循环渲染v-for keymap key双向绑定v-model 语法简洁v-model 语法糖或手动展开插槽slot 语法结构清晰v-slots 对象写法动态渲染能力依赖指令和 component函数调用、高阶组件、直接 JS 逻辑类型推断模板中 props 推断有限TSX 有完整类型检查学习门槛指令系统需要额外学习本质是 JS 语法我个人的经验是普通业务页面、表单页面、低代码配置页面模板仍然是优先级最高的选择可读性好IDE 识别也稳定但如果你在写组件库、封装通用表格/弹窗、做动态渲染引擎、或者业务里有大量“根据状态组合 UI”的场景JSX 的效率优势非常明显。两种情况并存才是 Vue 3 项目的常态不要为了用 JSX 而把简单页面也强行改写。5. 常见问题与避坑经验5.1 TSX 中 style 对象的类型报错写内联样式时直接写对象字面量经常报类型错误div style{{ background: #fff, fontSize: 14 }}在 React 中style 对象要求React.CSSProperties类型数字单位会被自动推断为 px但字符串14px也是合法的。解决方式是显式引入类型或确保对象符合 CSSProperties 结构import type { CSSProperties } from react; const style: CSSProperties { background: #fff, fontSize: 14, }; div style{style}Vue 3 的 JSX 中style 类型来自vue包同样可以import type { CSSProperties } from vue。常见报错是fontSize写成了font-size注意必须用驼峰。5.2 ref 在渲染函数中不会自动解包Vue 3 模板中ref变量会自动解包{{ count }}不需要写.value。但 JSX/渲染函数中返回的是一个普通虚拟节点树响应式变量的解包需要手动处理return () ( span{count.value}/span );如果忘了写.value页面会渲染出[object Object]或者出现类型告警。Vue 3.3 提供了响应式语法糖可以用$ref让 JSX 中也支持自动解包但需要编译插件配合日常项目按需使用。5.3 key 和 ref 的坑JSX 中 ref 是一个 prop在 React 的旧版本中ref是一个特殊属性不能作为 props 传递给子组件。React 19 中 ref 可以当作普通 prop 传递了。在 Vue 3 的 JSX 中ref 的用法与模板也不同const inputRef refHTMLInputElement | null(null); return () ( input ref{inputRef} / );注意这个ref传入的是响应式对象而不是直接赋 DOM 元素。如果你在自定义组件上使用 ref需要通过defineExpose暴露组件实例的方法和属性否则外部无法访问内部状态。5.4 事件类型推导失败的常见场景TSX 中最容易让人困惑的是事件参数类型“有时能推断有时不能”。比如在 Vue 3 中input value{value} onInput{(e) { // e 的类型可能被推断为 any 或 Event value (e.target as HTMLInputElement).value; }} /这里的e经常不会被精确推断为InputEvent需要手动断言e.target。React 中事件类型相对规范但自定义事件的类型也需要显式声明。建议在团队协作项目中给常用事件处理函数统一提取类型别名避免每个文件里都手写MouseEventHTMLButtonElement这种长类型。5.5 可维护性JSX 不等于可以随意写逻辑JSX 提供了强大的表达能力但不意味着你应该在 JSX 里塞一堆超长内联逻辑。我在实际项目中见过不少人把数据处理全部塞进map回调里一个 List 渲染写了二十几行维护者根本分不清哪些是业务计算、哪些是视图结构。比较好的做法是JSX 只保留与视图直接相关的条件渲染和循环渲染复杂的数据处理提前抽成函数或 computed。这和 Vue 模板中把业务逻辑放到 setup 里的思路是一致的。可读性的核心不在于用哪种语法而在于边界是否清晰。从 JSX 到 TSX再到 Vue 3 的 JSX 实践这几年的前端开发JSX 和 TSX 已经不只是 React 生态的名词而是现代组件化编程的通用表达能力之一。Vue 3 对 JSX 的支持让开发者多了一个选择也让模板与 JSX 的对比变得更有价值。无论你主力技术栈是什么理解 JSX 在编译层的本质、TSX 提供的类型约束、以及跨框架的语法映射规律在面对复杂渲染需求时都会更有底气。我个人在实际项目中保留的习惯是模板优先用于业务页面JSX 用于组件封装和动态渲染逻辑写 TSX 时给每个 props 接口都认真定义类型事件对象和泛型组件多花一点时间写好类型后续维护能省下大量排查问题的时间。希望对正在学习 JSX/TSX、或打算在 Vue 3 项目中尝试 JSX 的你有所帮助。

相关新闻

Comsol微流控两相流仿真实战:从水平集到液滴生成

Comsol微流控两相流仿真实战:从水平集到液滴生成

做微流控芯片这几年,我有一半时间都泡在两相流仿真里。T型通道里生成液滴、气液两相在蛇形流道中的分布、燃料电池流道里水的排出……这些现象背后全是一堆耦合的物理:流体流动、界面张力、润湿接触角、甚至温度场和电场。想靠肉眼和实验一遍遍试&#x…

2026/10/3 10:04:36 阅读更多 →
Flink实时推荐系统全链路拆解:从Kafka行为接入到Redis结果落地

Flink实时推荐系统全链路拆解:从Kafka行为接入到Redis结果落地

简介:一套基于Apache Flink的商品实时推荐系统完整项目源码,面向大数据方向的学生、开发者和推荐系统初学者。项目以Scala为主要语言,完整实现了从数据采集、预处理、特征工程到推荐算法、结果输出、系统优化的核心链路,并特别提供…

2026/10/3 10:04:36 阅读更多 →
微信小程序+SpringBoot乡村政务系统:从设计到部署全解析

微信小程序+SpringBoot乡村政务系统:从设计到部署全解析

看到这个标题,就知道是一套很典型的“毕业设计真实业务场景”组合:微信小程序做前端,SpringBoot做后端,场景落在乡村政务服务。说实在的,这类系统我在实际开发和带学生的过程中见过太多版本,但大多都存在一…

2026/10/3 10:04:36 阅读更多 →

最新新闻

Linux面试题精选:45个高频考点与实战解析

Linux面试题精选:45个高频考点与实战解析

你被问过这几个问题吗?ps aux和top到底该看哪个?chmod 777为什么会被面试官皱眉头?df显示磁盘满了但du找不到大文件,问题出在哪?这些年我面过不少候选人,也被别人面过。Linux面试题看起来满天飞&#xff0c…

2026/10/3 11:15:08 阅读更多 →
生产级Coding Agent调优实录:从Vibe Coding到稳定交付的最后一公里

生产级Coding Agent调优实录:从Vibe Coding到稳定交付的最后一公里

Vibe Coding 这个词从去年开始突然就火得不行,但大多数人对它的理解还停留在"用 AI 把想法变成代码"的层面。真正到了工程化落地的阶段,你会发现写一个能跑的 Demo 和交付一个能扛住生产环境压力的 Coding Agent,中间隔着的不是一两…

2026/10/3 11:15:07 阅读更多 →
从零训练大模型:反向传播到微型GPT与推理模型的工程实践

从零训练大模型:反向传播到微型GPT与推理模型的工程实践

过去一年里我被问到最多的一个问题,不是"哪个模型更好用",而是"我想认真搞AI,该从哪里开始"。问的人五花八门:写后端服务的、做数据产品的、刚毕业的学生。他们大部分已经能熟练调各家API,也跑过几…

2026/10/3 11:15:07 阅读更多 →
OpenShell:经典开始菜单与高效定制完全指南

OpenShell:经典开始菜单与高效定制完全指南

1. 项目概述:OpenShell到底解决什么问题1.1 一个被忽视的系统痛点如果你折腾过Windows 8以后的系统,大概率有过这种体验:明明装好了最新的系统,硬件配置也不差,但每次点击那个满屏磁贴的“开始”屏幕,或者面…

2026/10/3 11:15:07 阅读更多 →
数字IC后端项目实战:从congestion到低功耗的完整问题排查清单

数字IC后端项目实战:从congestion到低功耗的完整问题排查清单

做了快两年的数字IC后端项目,从28nm一路做到更先进的节点,最大的感受是:后端这个活儿,真正值钱的不是把一条流程流水线式跑通,而是每一次跑完flow之后,面对那一堆或红或黄的问题报告,能快速定位…

2026/10/3 11:15:07 阅读更多 →
金融级分布式数据库落地指南:从选型到避坑

金融级分布式数据库落地指南:从选型到避坑

简介:沙利文与头豹研究院联合发布的《2024年中国金融级分布式数据库市场跟踪报告》PDF文档,面向金融行业专业人士、数据库供应商、政策制定者与研究者,系统呈现分布式数据库市场动态及竞争格局。资源共1个PDF文件,压缩包5.58MB&am…

2026/10/3 11:14:07 阅读更多 →

日新闻

把回忆蒸馏成 AI 的浪漫实验:为什么你需要前任.skill 完整指南

把回忆蒸馏成 AI 的浪漫实验:为什么你需要前任.skill 完整指南

把回忆蒸馏成 AI 的浪漫实验:为什么你需要前任.skill 完整指南 【免费下载链接】ex-skill 前任 skill 项目地址: https://gitcode.com/gh_mirrors/exsk/ex-skill 前任.skill 是一个运行在 Claude Code 上的开源 Skill:导入微信、iMessage、短信、…

2026/10/3 0:00:27 阅读更多 →
45个经典Linux面试题:从命令到网络排障的完整考点解析

45个经典Linux面试题:从命令到网络排障的完整考点解析

刚开始带应届生的时候,我最头疼的就是他们拿着一摞Linux面试题背得滚瓜烂熟,一上机全露馅。后来自己从被面的人变成面别人的人,才慢慢摸清楚:Linux面试题考的根本不是答案本身,而是你面对一个不确定的系统问题时&#…

2026/10/3 0:01:28 阅读更多 →
SAP生产预留实战指南:MB21/MB23/MB25协同与MRP集成

SAP生产预留实战指南:MB21/MB23/MB25协同与MRP集成

简介:本资源是一份面向SAP ABAP开发人员、生产计划专员及ERP实施顾问的实操型操作指南,聚焦SAP生产预留核心业务场景,系统解决物料预留创建、查询、校验与批量处理等高频问题。文档以结构化方式覆盖预留背景原理、OMC2编码规则、工厂级参数配…

2026/10/3 0:01:28 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/10/3 9:14:33 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/10/3 9:47:50 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/10/3 9:42:31 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 10:36:31 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:35 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:36 阅读更多 →