Jinuss's blog Jinuss's blog
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

东流

Web、WebGIS技术博客
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 《React源码》笔记
  • JSX
东流
2026-01-15
目录

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

转换后:

// Babel/TypeScript 转换结果
import React from 'react';

function MyComponent() {
  return React.createElement('div', null, 'Hello');
}
1
2
3
4
5
6

# React 17+ 版本

// 不需要导入 React!
function MyComponent() {
  return <div>Hello</div>;
}
1
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. 转换机制的对比

# 旧转换器(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

# 新转换器(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

# 3. 如何启用新转换

# 使用 TypeScript

// tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx",  // React 17+ 新转换
    // 或
    "jsx": "react-jsxdev"  // 开发模式
  }
}
1
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

# 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

# 4.2 更小的包体积

// 旧转换:每个文件都包含完整的 React.createElement
// 新转换:按需导入,tree-shaking 更友好
1
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

# 5. 特殊情况处理

# 5.1 仍然需要导入的情况

// 如果使用 React 的其他 API,仍然需要导入
import { useState, useEffect } from 'react';
// 但不需要导入 React 本身
1
2
3

# 5.2 自定义 JSX 工厂

// 可以自定义 JSX 工厂函数
// tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "@emotion/react"  // 使用 emotion
  }
}
1
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

# 6.2 混合模式

// 可以同时使用新旧转换
// 旧组件
import React from 'react';

// 新组件
function NewComponent() {
  // 不需要导入 React
  return <div>New</div>;
}
1
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

# 迁移后

// 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

# 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

# 8.2 自动移除导入

# 使用 codemod 工具自动迁移
npx react-codemod update-react-imports
1
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

# 9.2 实际使用的函数

// 生产环境
import { jsx, jsxs } from 'react/jsx-runtime';

// 开发环境
import { jsxDEV, jsxsDEV } from 'react/jsx-dev-runtime';
1
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

# 10.2 库开发者的注意事项

// 库应该支持两种模式
export { jsx, jsxs } from 'react/jsx-runtime';
export { jsxDEV, jsxsDEV } from 'react/jsx-dev-runtime';
1
2
3

# 11. 完整迁移检查清单

  1. ✅ 升级 React 到 16.14+ 或 17+
  2. ✅ 更新 TypeScript 配置:jsx: "react-jsx"
  3. ✅ 更新 Babel 配置:runtime: "automatic"
  4. ✅ 移除不必要的 React 导入
  5. ✅ 更新 ESLint 规则
  6. ✅ 测试所有组件功能正常

# 总结

React 17 的新 JSX 转换带来了以下好处:

  1. 开发体验改善:不再需要手动导入 React
  2. 包体积减小:自动导入更高效
  3. 性能提升:新的 jsx 和 jsxs 函数
  4. 未来兼容:为 React 后续版本打下基础

这个变化是 React 生态系统的重要演进,使开发更加简洁,同时为后续的优化铺平了道路。

编辑 (opens new window)
上次更新: 2026/01/15, 06:03:07
最近更新
01
元素创建
01-15
02
ice.js中Model数据初始化原理
12-10
03
Pinia中实现监听action的原理
11-28
更多文章>
Theme by Vdoing | Copyright © 2024-2026 东流 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式