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 生态系统的重要演进,使开发更加简洁,同时为后续的优化铺平了道路。

相关推荐
Pedantic6 小时前
SwiftUI 手势层级(Gesture Hierarchy)详解
前端
飘尘6 小时前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
一颗烂土豆6 小时前
Meshopt 压缩深度解析,为什么它比 Draco 更快
前端·javascript·webgl
YFF菲菲兔7 小时前
调度系统和调和系统的桥梁
react.js
浏览器工程师7 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
雨季mo浅忆7 小时前
VSCode自动格式化三要素
前端
爱勇宝8 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员
kyriewen8 小时前
同事每天催我 Code Review,我写了个脚本让 AI 替我 review PR——现在他反过来催 AI 了
前端·javascript·ai编程
user205855615181311 小时前
Windows 项目安装时报 `node-sass` 错误,如何快速处理
前端