React16与React17+的JSX转换差异

React 16 与 React 17+ 的 JSX 转换差异

相比React16,React 17引入了一项重要的变化,使得 TSX/JSX 文件中不再需要显式地导入 React。让我详细解释这个变化。

1. 历史背景

React 16 及之前版本

jsx 复制代码
// 必须导入 React
import React from 'react';

function MyComponent() {
  return <div>Hello</div>;
}

转换后

javascript 复制代码
// Babel/TypeScript 转换结果
import React from 'react';

function MyComponent() {
  return React.createElement('div', null, 'Hello');
}

React 17+ 版本

jsx 复制代码
// 不需要导入 React!
function MyComponent() {
  return <div>Hello</div>;
}

转换后

javascript 复制代码
// 新转换器会自动导入
import { jsx as _jsx } from 'react/jsx-runtime';

function MyComponent() {
  return _jsx('div', { children: 'Hello' });
}

2. 转换机制的对比

旧转换器(React 16)

javascript 复制代码
// JSX: <div className="container">Hello</div>
// 转换后:
React.createElement('div', { className: 'container' }, 'Hello');

// JSX: <MyComponent prop="value" />
// 转换后:
React.createElement(MyComponent, { prop: 'value' });

新转换器(React 17+)

javascript 复制代码
// 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' });

3. 如何启用新转换

使用 TypeScript

json 复制代码
// tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx",  // React 17+ 新转换
    // 或
    "jsx": "react-jsxdev"  // 开发模式
  }
}

使用 Babel

javascript 复制代码
// .babelrc
{
  "presets": [
    [
      "@babel/preset-react",
      {
        "runtime": "automatic"  // 自动导入
        // 或 "runtime": "classic"  // 传统方式
      }
    ]
  ]
}

4. 新转换的优势

4.1 不再需要手动导入 React

javascript 复制代码
// ❌ 以前:必须导入
import React from 'react';

// ✅ 现在:不需要导入
function App() {
  return (
    <>
      <Header />
      <Main />
      <Footer />
    </>
  );
}

4.2 更小的包体积

javascript 复制代码
// 旧转换:每个文件都包含完整的 React.createElement
// 新转换:按需导入,tree-shaking 更友好

4.3 性能改进

javascript 复制代码
// 新转换自动优化
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 })
  ]
});

5. 特殊情况处理

5.1 仍然需要导入的情况

javascript 复制代码
// 如果使用 React 的其他 API,仍然需要导入
import { useState, useEffect } from 'react';
// 但不需要导入 React 本身

5.2 自定义 JSX 工厂

typescript 复制代码
// 可以自定义 JSX 工厂函数
// tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "@emotion/react"  // 使用 emotion
  }
}

6. 向后兼容性

6.1 逐步迁移

json 复制代码
// package.json - 可以逐步升级
{
  "dependencies": {
    "react": "^16.14.0",  // 16.14+ 支持新转换
    "react-dom": "^16.14.0"
  }
}

6.2 混合模式

javascript 复制代码
// 可以同时使用新旧转换
// 旧组件
import React from 'react';

// 新组件
function NewComponent() {
  // 不需要导入 React
  return <div>New</div>;
}

7. 实际迁移示例

迁移前

typescript 复制代码
// 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'));

迁移后

typescript 复制代码
// 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 />);

8. 工具更新

8.1 ESLint 配置

javascript 复制代码
// .eslintrc.js
module.exports = {
  rules: {
    'react/jsx-uses-react': 'off',      // 不再需要
    'react/react-in-jsx-scope': 'off',  // 不再需要
  }
};

8.2 自动移除导入

bash 复制代码
# 使用 codemod 工具自动迁移
npx react-codemod update-react-imports

9. 新转换的内部机制

9.1 自动导入的源代码

javascript 复制代码
// react/jsx-runtime.js (简化版)
export { jsx, jsxs, jsxDEV } from './src/jsx/ReactJSX';

// react/jsx-dev-runtime.js (开发版)
export { jsxDEV, jsxsDEV } from './src/jsx/ReactJSX';

9.2 实际使用的函数

javascript 复制代码
// 生产环境
import { jsx, jsxs } from 'react/jsx-runtime';

// 开发环境
import { jsxDEV, jsxsDEV } from 'react/jsx-dev-runtime';

10. 常见问题解决

10.1 错误:"React is not defined"

javascript 复制代码
// 解决方案1:升级 React 版本
"react": "^17.0.0"

// 解决方案2:更新 Babel 配置
// .babelrc
{
  "presets": [
    ["@babel/preset-react", { "runtime": "automatic" }]
  ]
}

// 解决方案3:更新 TypeScript 配置
// tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx"
  }
}

10.2 库开发者的注意事项

typescript 复制代码
// 库应该支持两种模式
export { jsx, jsxs } from 'react/jsx-runtime';
export { jsxDEV, jsxsDEV } from 'react/jsx-dev-runtime';

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. 性能提升 :新的 jsxjsxs 函数
  4. 未来兼容:为 React 后续版本打下基础

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

相关推荐
m0_748254662 小时前
HTML DOM - 修改 HTML 内容的方法
前端·html
爱吃香菜i2 小时前
数据连接开发设计文档
前端·javascript
冴羽2 小时前
现代 CSS 颜色使用指南
前端·javascript·css
cj81402 小时前
动态表单与静态表单性能比较
前端
han_2 小时前
为啥 Array.isArray 判断数组最靠谱?
前端·javascript
前端糕手2 小时前
面试高频版
前端·html
哈哈你是真的厉害3 小时前
React Native 鸿蒙跨平台开发:Badge 徽标
react native·react.js·harmonyos
沛沛老爹3 小时前
Web转AI决策篇 Agent Skills vs MCP:选型决策矩阵与评估标准
java·前端·人工智能·架构·rag·web转型
字节架构前端3 小时前
媒体采集标准草案 与 Chromium 音频采集实现简介
前端·chrome·音视频开发