React16与React17+的JSX转换差异
# React 16 与 React 17+ 的 JSX 转换差异
相比React16,React 17引入了一项重要的变化,使得 TSX/JSX 文件中不再需要显式地导入 React。让我详细解释这个变化。
# 1. 历史背景
# React 16 及之前版本
// 必须导入 React
import React from 'react';
function MyComponent() {
return <div>Hello</div>;
}
1
2
3
4
5
6
2
3
4
5
6
转换后:
// Babel/TypeScript 转换结果
import React from 'react';
function MyComponent() {
return React.createElement('div', null, 'Hello');
}
1
2
3
4
5
6
2
3
4
5
6
# React 17+ 版本
// 不需要导入 React!
function MyComponent() {
return <div>Hello</div>;
}
1
2
3
4
2
3
4
转换后:
// 新转换器会自动导入
import { jsx as _jsx } from 'react/jsx-runtime';
function MyComponent() {
return _jsx('div', { children: 'Hello' });
}
1
2
3
4
5
6
2
3
4
5
6
# 2. 转换机制的对比
# 旧转换器(React 16)
// JSX: <div className="container">Hello</div>
// 转换后:
React.createElement('div', { className: 'container' }, 'Hello');
// JSX: <MyComponent prop="value" />
// 转换后:
React.createElement(MyComponent, { prop: 'value' });
1
2
3
4
5
6
7
2
3
4
5
6
7
# 新转换器(React 17+)
// JSX: <div className="container">Hello</div>
// 转换后:
import { jsx as _jsx } from 'react/jsx-runtime';
_jsx('div', { className: 'container', children: 'Hello' });
// JSX: <MyComponent prop="value" />
// 转换后:
import { jsx as _jsx } from 'react/jsx-runtime';
_jsx(MyComponent, { prop: 'value' });
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 3. 如何启用新转换
# 使用 TypeScript
// tsconfig.json
{
"compilerOptions": {
"jsx": "react-jsx", // React 17+ 新转换
// 或
"jsx": "react-jsxdev" // 开发模式
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 使用 Babel
// .babelrc
{
"presets": [
[
"@babel/preset-react",
{
"runtime": "automatic" // 自动导入
// 或 "runtime": "classic" // 传统方式
}
]
]
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 4. 新转换的优势
# 4.1 不再需要手动导入 React
// ❌ 以前:必须导入
import React from 'react';
// ✅ 现在:不需要导入
function App() {
return (
<>
<Header />
<Main />
<Footer />
</>
);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 4.2 更小的包体积
// 旧转换:每个文件都包含完整的 React.createElement
// 新转换:按需导入,tree-shaking 更友好
1
2
2
# 4.3 性能改进
// 新转换自动优化
const element = (
<div>
<span>Static</span>
<span>{dynamic}</span>
</div>
);
// 优化转换:
import { jsx as _jsx, jsxs as _jsxs } from 'react/jsx-runtime';
// 静态部分(jsxs 优化静态子元素)
const element = _jsxs('div', {
children: [
_jsx('span', { children: 'Static' }),
_jsx('span', { children: dynamic })
]
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 5. 特殊情况处理
# 5.1 仍然需要导入的情况
// 如果使用 React 的其他 API,仍然需要导入
import { useState, useEffect } from 'react';
// 但不需要导入 React 本身
1
2
3
2
3
# 5.2 自定义 JSX 工厂
// 可以自定义 JSX 工厂函数
// tsconfig.json
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@emotion/react" // 使用 emotion
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 6. 向后兼容性
# 6.1 逐步迁移
// package.json - 可以逐步升级
{
"dependencies": {
"react": "^16.14.0", // 16.14+ 支持新转换
"react-dom": "^16.14.0"
}
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 6.2 混合模式
// 可以同时使用新旧转换
// 旧组件
import React from 'react';
// 新组件
function NewComponent() {
// 不需要导入 React
return <div>New</div>;
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 7. 实际迁移示例
# 迁移前
// App.tsx (React 16)
import React from 'react';
import ReactDOM from 'react-dom';
const App: React.FC = () => {
return (
<div className="app">
<h1>Hello World</h1>
</div>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 迁移后
// App.tsx (React 17+)
import { createRoot } from 'react-dom/client';
function App() { // 不需要 React.FC
return (
<div className="app">
<h1>Hello World</h1>
</div>
);
}
const root = createRoot(document.getElementById('root')!);
root.render(<App />);
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 8. 工具更新
# 8.1 ESLint 配置
// .eslintrc.js
module.exports = {
rules: {
'react/jsx-uses-react': 'off', // 不再需要
'react/react-in-jsx-scope': 'off', // 不再需要
}
};
1
2
3
4
5
6
7
2
3
4
5
6
7
# 8.2 自动移除导入
# 使用 codemod 工具自动迁移
npx react-codemod update-react-imports
1
2
2
# 9. 新转换的内部机制
# 9.1 自动导入的源代码
// react/jsx-runtime.js (简化版)
export { jsx, jsxs, jsxDEV } from './src/jsx/ReactJSX';
// react/jsx-dev-runtime.js (开发版)
export { jsxDEV, jsxsDEV } from './src/jsx/ReactJSX';
1
2
3
4
5
2
3
4
5
# 9.2 实际使用的函数
// 生产环境
import { jsx, jsxs } from 'react/jsx-runtime';
// 开发环境
import { jsxDEV, jsxsDEV } from 'react/jsx-dev-runtime';
1
2
3
4
5
2
3
4
5
# 10. 常见问题解决
# 10.1 错误:"React is not defined"
// 解决方案1:升级 React 版本
"react": "^17.0.0"
// 解决方案2:更新 Babel 配置
// .babelrc
{
"presets": [
["@babel/preset-react", { "runtime": "automatic" }]
]
}
// 解决方案3:更新 TypeScript 配置
// tsconfig.json
{
"compilerOptions": {
"jsx": "react-jsx"
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 10.2 库开发者的注意事项
// 库应该支持两种模式
export { jsx, jsxs } from 'react/jsx-runtime';
export { jsxDEV, jsxsDEV } from 'react/jsx-dev-runtime';
1
2
3
2
3
# 11. 完整迁移检查清单
- ✅ 升级 React 到 16.14+ 或 17+
- ✅ 更新 TypeScript 配置:
jsx: "react-jsx" - ✅ 更新 Babel 配置:
runtime: "automatic" - ✅ 移除不必要的 React 导入
- ✅ 更新 ESLint 规则
- ✅ 测试所有组件功能正常
# 总结
React 17 的新 JSX 转换带来了以下好处:
- 开发体验改善:不再需要手动导入 React
- 包体积减小:自动导入更高效
- 性能提升:新的
jsx和jsxs函数 - 未来兼容:为 React 后续版本打下基础
这个变化是 React 生态系统的重要演进,使开发更加简洁,同时为后续的优化铺平了道路。
编辑 (opens new window)
上次更新: 2026/01/15, 06:03:07