尽管之前React 17.0的主版本号已经说过了,17.0是一个没有新功能的,主要侧重于React本身的升级,把17.0比作一个垫脚石版本。昨天React发布了blog,提供了一套全新的JSX转换。我这里根据官方blog做一个简单地介绍。
什么是JSX转换?
大家应该都知道,现有的浏览器都是无法直接解译JSX的,所以大多数React用户都需要使用Babel或者TypeScript之类的编译器来将JSX转换为浏览器能够理解的JavaScript语言。许多预配置的工具箱(如:Create React App 或者Next.js)内部也有JSX的转换。
React 17.0的正式版发布在即,尽管React团队想对JSX的转换进行改进,但React团队不想打破现有的配置。这就是为什么React团队与Babel合作,为想要升级的开发者提供了一个全新的JSX转换的重写版本。
本次升级到新的版本是可选的,但升级它会为你带来一些好处:
- 通过全新的转换,你可以单独使用JSX而无需引入React.
- 根据你的配置,JSX的编译输出可能会稍微改善bundle的大小.
- 它将实现未来的改进,减少你需要学习React概念的数量。
此次升级不会改变JSX语法,因此不是必须的。旧的JSX转换将继续正常工作。没有计划取消对它的支持。
React 17的RC版本已经引入了对全新的转换的支持。所以你可以尝试一下。为了让大家更容易使用,在React17发布之后,我们计划支持React 16.x, React 15.x和React 0.14.x。你可以在下面找到不同工具的升级说明。
现在,让我们仔细看看新旧转换之间的区别。
新转换有何不同?
当你使用JSX时,编译器会将其转换为浏览器可以理解的React函数调用。旧的JSX转换将JSX转换为React.createElement(...)
调用。
例如,假设源代码如下:
import React from 'react';
function App() {
return Hello World
;
}
在后台,旧的JSX转换为常规的javaScript:
import React from 'react';
function App() {
return React.createElement('h1', null, 'Hello world');
}
注意
无需更改代码。我们将介绍JSX转换如何讲你的JSX源码转换为浏览器能理解的JavaScript代码。
但是,这并不完美:
- 因为如果使用JSX,则需要在React的环境下,因为JSX会被编译成React.createElement.
- 有一些React.createElement不允许做性能的改进和简化。
为了解决这些问题,React17在React包中引入了两个新的入口,这些入口只会被Babel和TypeScript等编译器使用。新的JSX转换没有将JSX转换为React.createElement,而是自动从React包中引入新的入口函数并调用。例如:
function App() {
return Hello World
;
}
现在将转换为:
// Inserted by a compiler (don't import it yourself!)
import {jsx as _jsx} from 'react/jsx-runtime';
function App() {
return _jsx('h1', { children: 'Hello world' });
}
注意,此时源代码无需引入React即可使用JSX了!(但是我们任然需要导入React才能使用Hooks或者其他导出。)
此次变化与现在所有的JSX代码完全兼容,因此无需修改组件。如果你对此感兴趣,你可以查看RFC了解全新转换工作的具体细节。
注意
react/jsx-runtime和react/jsx-dev-runtime中的函数只能由编译器转换使用。如果需要在代码中手动创建元素,则应该继续使用React.createElement。他将继续工作,不会消失。
如何升级到新的JSX转换
如果你还没有准备好升级到全新的JSX转换,或者你正在为其他库使用JSX,请不要担心,旧的转换不会被删除,并将继续支持。
如果要升级,则需要两件事:
- 一个支持全新转换的React版本(当前,只有React 17RC 支持它,但是在React17.0发布后,我们计划针对16.x 15.x 0.14.x发布兼容版本。)
- 兼容的编译器(请看下面关于不同工具的说明)。
由于新的JSX转换不依赖React环境,我们准备了一个自动化脚本,该脚本将从您的代码中删除不必要的导入。
Create React App
Create React App 已对其做了兼容支持。并将在即将到来的V4.0版本中提供支持,该版本现在还处于测试阶段。
Next.js
Next.js的v9.5.3+会使用新的转换来兼容React版本。
Gatsby
Gatsby的v2.24.5+会使用新的转换来兼容React版本。
注意
如果在Gatsby遇到error,请升级到17.0.0-rc.2,请运行npm update以对其进行修复。
手动配置Babel
Babel v7.9.0及更高版本提供了对新JSX转换的支持。
首先,你需要更新至最新版本的Babel和transform插件。
如果您正在使用@babel/plugin-transform-react-jsx:
# for npm users
npm update @babel/core @babel/plugin-transform-react-jsx
# for yarn users
yarn upgrade @babel/core @babel/plugin-transform-react-jsx
如果您正在使用@babel/preset-react:
# for npm users
npm update @babel/core @babel/preset-react
# for yarn users
yarn upgrade @babel/core @babel/preset-react
当前,旧的转换("runtime": "classic")是默认选项。如果要启用新的转换,您可以将{"runtime": "automatic"}作为传递给@babel/plugin-transform-react-jsx或@babel/preset-react的选项:
// If you are using @babel/preset-react
{
"presets": [
["@babel/preset-react", {
"runtime": "automatic"
}]
]
}
// If you're using @babel/plugin-transform-react-jsx
{
"plugins": [
["@babel/plugin-transform-react-jsx", {
"runtime": "automatic"
}]
]
}
从Babel 8开始 "automatic"会将两个插件默认集成在runtime中。有关更多信息,请查看Babel文档中的@ babel / plugin-transform-react-jsx和@ babel / preset-react。
注意
如果你在使用JSX时,使用React以外的库,你可以使用importSource
选项从该库中引入,前提是他提供了必要的入口。或者你可以继续使用经典的转换,它将继续被支持。
ESLint
如果你正在使用eslint-plugin-react,其中的react/jsx-uses-react和react/react-in-jsx-scope规则将不再需要,可以关闭或者删除它们。
{
// ...
"rules": {
// ...
"react/jsx-uses-react": "off",
"react/react-in-jsx-scope": "off"
}
}
TypeScript
TypeScript将在v4.1 beta中支持新的JSX转换。
Flow
Flow将在v0.126.0及更高版本中的新JSX转换。
删除未使用的React导入
因为新的JSX转换会自动引入必要的react/jsx-runtime函数,因此当你使用JSX时,将无需再引入React。这可能会导致你的代码中有未使用的React导入。保留它们并没有什么坏处,但是如果你想删除它们,我们建议运行“ codemod”脚本以自动删除它们:
cd your_project
npx react-codemod update-react-imports
注意
如果你在运行codemod时遇到错误,请在npx react-codemod update-react-imports要求您选择其他JavaScript环境。尤其是选择"javaScript with Flow"时,即使你没有使用Flow,也可以选择它。
请记住,在codemod输出并不总是匹配你的项目的编码风格,所以你可能需要运行Prettier在codemod结束后以保证编码风格一致。
运行此codemod将:
- 升级到新的JSX转换后,删除所有未使用的React导入。
- 改变所有的React的默认引入,更改为非结构化命名导入。例如:import React from "react" 改为import { useState } from "react",这将是未来的首选风格。codemod 不会影响import * as React from "react" 这也是一个有效的风格,磨人的导入将继续在React17中工作,但从长远来看,我们鼓励不再使用它们。
例如:
import React from 'react';
function App() {
return Hello World
;
}
将被替换为:
function App() {
return Hello World
;
}
如果你使用了React的其他导入,比如hook,那么codemod将把他们转化为具名导入。
例如:
import React from 'react';
function App() {
const [text, setText] = React.useState('Hello World');
return {text}
;
}
将被替换为:
import { useState } from 'react';
function App() {
const [text, setText] = useState('Hello World');
return {text}
;
}
除了清理未使用的导入,次工具还可帮你为未来的React(不是React17)主要版本做准备,该版本将支持ES模块,并且没有默认导出。