Hi,我是前端人类学 (之前叫布兰妮甜)!
"JSX 既不是 HTML,也不是字符串;它只是 JavaScript 的一种更友好的写法。" ------React 官方文档
在 2025 年的前端开发语境里,JSX 几乎成了 React 的代名词。然而,很多初学者仍然把它当成"另一种语言"。本文尝试用一条清晰的脉络回答:JSX 与 JavaScript 到底是什么关系?它们如何分工、如何协同,以及在实际工程里有哪些最新实践。
文章目录
[一、血统:JSX 首先是一段合法的 JavaScript 程序](#一、血统:JSX 首先是一段合法的 JavaScript 程序)
[1.1 语法扩展,而非新语言](#1.1 语法扩展,而非新语言)
[1.2 文件扩展名只是"提示"](#1.2 文件扩展名只是“提示”)
[二、转译:从 JSX 到 JavaScript 的三次"变形"](#二、转译:从 JSX 到 JavaScript 的三次“变形”)
[2.1 经典转换:`React.createElement`](#2.1 经典转换:React.createElement)
[2.2 新 JSX Transform:React 17+ 的优化](#2.2 新 JSX Transform:React 17+ 的优化)
[2.3 TypeScript 中的 JSX 模式](#2.3 TypeScript 中的 JSX 模式)
[三、语义:JSX 让 JavaScript 获得了"声明式 UI"表达能力](#三、语义:JSX 让 JavaScript 获得了“声明式 UI”表达能力)
[3.1 声明式 vs 命令式](#3.1 声明式 vs 命令式)
[3.2 JSX 的 JavaScript 完全能力](#3.2 JSX 的 JavaScript 完全能力)
[四、边界:JSX 做不到的那些 JavaScript 事](#四、边界:JSX 做不到的那些 JavaScript 事)
[五、生态:2025 年的工程化实践清单](#五、生态:2025 年的工程化实践清单)
一、血统:JSX 首先是一段合法的 JavaScript 程序
1.1 语法扩展,而非新语言
JSX 是 ECMAScript 的 语法扩展 (syntax extension) 。按照规范,任何符合 JSX 语法的源码片段 在词法和语法层面都是合法的 JavaScript。因此:
它不会引入新的关键字,也不会破坏 var / let / const 的词法作用域规则;
所有 JavaScript 表达式在 JSX 中依旧可用(花括号 {} 内);
任何 JavaScript 运行时可以忽略 JSX 语法,直接报错或跳过(因为不认识的 AST 节点)。
1.2 文件扩展名只是"提示"
.jsx 与 .js 的差异并非规范强制,而是工程约定:
扩展名
典型用途
是否需要编译
备注
.js
普通 JavaScript、工具函数、无 JSX 的 React 组件
可选
若文件里写了 JSX,则仍需 Babel/TypeScript 处理
.jsx
包含 JSX 的 UI 组件
必须
让开发者、IDE、Linter 一眼看出"这是 JSX"
二、转译:从 JSX 到 JavaScript 的三次"变形"
浏览器、Node.js 本身并不认识 JSX,因此源码上线前需要"翻译"。当前主流工具链有 Babel、TypeScript Compiler (tsc)、esbuild、swc,它们做的事可以概括为三步:
阶段
输入
输出
工具
① 类型擦除
TSX (TypeScript + JSX)
JSX
tsc --jsx preserve
② JSX 变换
JSX
React.createElement / _jsx() 调用
Babel @babel/plugin-transform-react-jsx
③ 语法降级
ES2025
ES2015 / ES5
Babel preset-env / swc
2.1 经典转换:React.createElement
这是 React 16 及以前的默认做法:
jsx
复制代码
// 源码
function Button({ label }) {
return ;
}
// Babel 产物 (旧)
import React from 'react';
function Button({ label }) {
return React.createElement('button', null, label);
}
2.2 新 JSX Transform:React 17+ 的优化
2020 年底,React 团队引入 "新 JSX 转换":
js
复制代码
// Babel 产物 (新)
import { jsx as _jsx } from 'react/jsx-runtime';
function Button({ label }) {
return _jsx('button', { children: label });
}
带来的好处:
无需手动 import React(除非使用 Hooks);
包体更小 :每个文件减少一次 React 依赖;
运行时更快 :_jsx 内部可以跳过一些 createElement 的兼容逻辑。
2.3 TypeScript 中的 JSX 模式
tsc 提供 4 种 --jsx 选项,决定了第 ① 步的产物:
preserve:完全保留 JSX,交给 Babel 做后续;
react:直接编译成 React.createElement;
react-jsx:输出 _jsx 调用;
react-jsxdev:开发模式,额外注入调试信息。
三、语义:JSX 让 JavaScript 获得了"声明式 UI"表达能力
3.1 声明式 vs 命令式
在纯 JavaScript 里,更新 DOM 通常是命令式:
js
复制代码
const h1 = document.createElement('h1');
h1.textContent = 'Hello';
document.body.appendChild(h1);
JSX 把同样的意图声明式地写进返回值:
jsx
复制代码
function Greeting() {
return
Hello
;}
React 负责把这段"描述"同步到真实 DOM。开发者不再关心如何更新 ,只关心长什么样。
3.2 JSX 的 JavaScript 完全能力
由于 JSX 只是语法糖,你可以在标签属性、子元素中写任意 JavaScript:
jsx
复制代码
function Card({ user }) {
return (
{user.posts.map(p =>
);
}
四、边界:JSX 做不到的那些 JavaScript 事
运行时自省:JSX 编译后就变成函数调用,无法再像模板字符串那样在运行时解析 AST。
多语言语法:不能在 JSX 中直接写 Python / Rust 表达式;而 JavaScript 的 Tagged Template 可以。
非 React 世界:Vue、Svelte、Solid 都提供了自己的 JSX 适配层,但语义不尽相同;脱离框架使用 JSX 需要额外运行时。
五、生态:2025 年的工程化实践清单
场景
推荐做法
新项目
React 18 + TypeScript + Vite + SWC(或 Babel)启用 react-jsx 转换
旧项目迁移
用 npx react-codemod update-react-imports 一键移除冗余 import React
服务端渲染 (Next.js)
Next.js 14 已默认开启新 JSX Transform,无需额外配置
非 React JSX(如 Solid)
在 vite.config.ts 里替换 @vitejs/plugin-solid,对应 JSX factory 指向 solid-js/h
Lint & 格式化
ESLint @eslint/jsx + Prettier 3,确保 .jsx/.tsx 文件规则一致
JSX 是 JavaScript 的语法扩展,它通过编译期转换,把类 HTML 的声明式结构映射到 JavaScript 函数调用,从而让 React(或其他库)在运行时可以高效地构建、更新用户界面。 因此,学习 JSX 从来不意味着在"多学一门语言",而是掌握一种更贴合 UI 思维的 JavaScript 写法。