北京時(shí)間 9 月 23 日凌晨,React 團(tuán)隊(duì)發(fā)布了關(guān)于全新 JSX 轉(zhuǎn)換的博客,同時(shí)發(fā)布了 React 17.0.0-rc.2 版本,新的 JSX 轉(zhuǎn)換不再依賴 React 環(huán)境,在轉(zhuǎn)換時(shí)會(huì)自動(dòng)引入新的 runtime。
原文鏈接:reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html
作者:Luna Ruan
譯者:QC-L
譯文首發(fā)于 React 中文文檔博客,歡迎關(guān)注 https://zh-hans.reactjs.org 或者關(guān)注印記中文公眾號(hào)。
雖然 React 17 并未包含新特性[1],但它將提供一個(gè)全新版本的 JSX 轉(zhuǎn)換。本文中,我們將為你描述它是什么以及如何使用。
何為 JSX 轉(zhuǎn)換?
在瀏覽器中無法直接使用 JSX,所以大多數(shù) React 開發(fā)者需依靠 Babel 或 TypeScript 來將 JSX 代碼轉(zhuǎn)換為 JavaScript。許多包含預(yù)配置的工具,例如 Create React App 或 Next.js,在其內(nèi)部也引入了 JSX 轉(zhuǎn)換。
React 17 發(fā)布在即,盡管我們想對(duì) JSX 的轉(zhuǎn)換進(jìn)行改進(jìn),但我們不想打破現(xiàn)有的配置。于是我們選擇與Babel[2] 合作,為想要升級(jí)的開發(fā)者提供了一個(gè)全新版本的,重構(gòu)過的 JSX 轉(zhuǎn)換。
升級(jí)至全新的轉(zhuǎn)換完全是可選的,但升級(jí)它會(huì)為你帶來一些好處:
- 使用全新的轉(zhuǎn)換,你可以單獨(dú)使用 JSX 而無需引入 React。
- 根據(jù)你的配置,JSX 的編譯輸出可能會(huì)略微改善 bundle 的大小。
- 它將減少你需要學(xué)習(xí) React 概念的數(shù)量,以備未來之需。
此次升級(jí)不會(huì)改變 JSX 語法,也并非必須。舊的 JSX 轉(zhuǎn)換將繼續(xù)工作,沒有計(jì)劃取消對(duì)它的支持。
React 17 的 RC 版本[3] 已經(jīng)引入了對(duì)全新 transform 的支持,所以你可以嘗試一下!為了讓大家更容易使用,在 React 17 正式發(fā)布后,我們還計(jì)劃將其支持 React 16.x,React 15.x 以及 React 0.14x。你可以在下方[4]找到不同環(huán)境的升級(jí)說明。
接下來,我們來仔細(xì)對(duì)比新舊轉(zhuǎn)換的區(qū)別。
新的轉(zhuǎn)換有何不同?
當(dāng)你使用 JSX 時(shí),編譯器會(huì)將其轉(zhuǎn)換為瀏覽器可以理解的 React 函數(shù)調(diào)用。舊的 JSX 轉(zhuǎn)換會(huì)把 JSX 轉(zhuǎn)換為 React.createElement(...)
調(diào)用。
例如,假設(shè)源代碼如下:
import React from 'react';
function App() {
return <h1>Hello World</h1>;
}
舊的 JSX 轉(zhuǎn)換會(huì)將上述代碼變成普通的 JavaScript 代碼:
import React from 'react';
function App() {
return React.createElement('h1', null, 'Hello world');
}
注意
無需改變?cè)创a。我們將介紹 JSX 轉(zhuǎn)換如何將你的 JSX 源碼變成瀏覽器可以理解的 JavaScript 代碼。
然而,這并不完美:
- 如果使用 JSX,則需在
React
的環(huán)境下,因?yàn)?JSX 將被編譯成React.createElement
。 - 有一些
React.createElement
無法做到的 性能優(yōu)化和簡化[5]。
為了解決這些問題,React 17 在 React 的 package 中引入了兩個(gè)新入口,這些入口只會(huì)被 Babel 和 TypeScript 等編譯器使用。新的 JSX 轉(zhuǎn)換不會(huì)將 JSX 轉(zhuǎn)換為 React.createElement
,而是自動(dòng)從 React 的 package 中引入新的入口函數(shù)并調(diào)用。例如:
function App() {
return <h1>Hello World</h1>;
}
現(xiàn)在將轉(zhuǎn)換為:
// 由編譯器引入(禁止自己引入!)
import {jsx as _jsx} from 'react/jsx-runtime';
function App() {
return _jsx('h1', { children: 'Hello world' });
}
注意,此時(shí)源代碼無需引入 React 即可使用 JSX 了?。ǖ孕枰?React,以便使用 React 提供的 Hook 或其他導(dǎo)出。)
此變化與所有現(xiàn)有 JSX 代碼兼容,所以你無需修改組件。如果你對(duì)此感興趣,你可以查看 RFC[6] 了解全新轉(zhuǎn)換工作的具體細(xì)節(jié)。
注意
react/jsx-runtime
和react/jsx-dev-runtime
中的函數(shù)只能由編譯器轉(zhuǎn)換使用。如果你需要在代碼中手動(dòng)創(chuàng)建元素,你可以繼續(xù)使用React.createElement
。它將繼續(xù)工作,不會(huì)消失。
如何升級(jí)至新的轉(zhuǎn)換
如果你還沒準(zhǔn)備好升級(jí)為全新的 JSX 轉(zhuǎn)換,或者你正在為其他庫使用 JSX,請(qǐng)不要擔(dān)心,舊的轉(zhuǎn)換不會(huì)被移除,并將繼續(xù)支持。
如果你想升級(jí),你需要準(zhǔn)備兩件事:
- 支持新轉(zhuǎn)換的 React 版本(目前,只有 React 17 的 RC 版本[7] 支持它,但是 React 17.0 發(fā)布后,我們計(jì)劃針對(duì) 0.14.x、15.x 以及 16.x 做兼容。)
- 一個(gè)兼容新轉(zhuǎn)換的編譯器(請(qǐng)看下面關(guān)于不同工具的說明)。
由于新的 JSX 轉(zhuǎn)換不依賴 React 環(huán)境,我們準(zhǔn)備了一個(gè)自動(dòng)腳本[8],用于移除你代碼中不必要的引入。
Create React App
Create React App 已對(duì)其做兼容支持[9],并將在 即將發(fā)布的 v4.0 版本[10]中提供,該版本處于測(cè)試階段。
Next.js
Next.js 的 v9.5.3[11]+ 會(huì)使用新的轉(zhuǎn)換來兼容 React 版本。
Gatsby
Gatsby 的 v2.24.5[12]+ 會(huì)使用新的轉(zhuǎn)換來兼容 React 版本。
注意
如果你在 Gatsby 中遇到 error[13],請(qǐng)升級(jí)至17.0.0-rc.2
,運(yùn)行npm update
解決此問題。
手動(dòng)配置 Babel
Babel 的 v7.9.0[14] 及以上版本可支持全新的 JSX 轉(zhuǎn)換。
首先,你需要更新至最新版本的 Babel 和 transform 插件。
如果你使用的是 @babel/plugin-transform-react-jsx
:
# npm 用戶
npm update @babel/core @babel/plugin-transform-react-jsx
# yarn 用戶
yarn upgrade @babel/core @babel/plugin-transform-react-jsx
如果你使用的是 @babel/preset-react
:
# npm 用戶
npm update @babel/core @babel/preset-react
# yarn 用戶
yarn upgrade @babel/core @babel/preset-react
目前,舊的轉(zhuǎn)換的默認(rèn)選項(xiàng)為("runtime": "classic"
)。如需啟用新的轉(zhuǎn)換,你可以使用 {"runtime": "automatic"}
作為 @babel/plugin-transform-react-jsx
或 @babel/preset-react
的選項(xiàng):
// 如果你使用的是 @babel/preset-react
{
"presets": [
["@babel/preset-react", {
"runtime": "automatic"
}]
]
}
// 如果你使用的是 @babel/plugin-transform-react-jsx
{
"plugins": [
["@babel/plugin-transform-react-jsx", {
"runtime": "automatic"
}]
]
}
從 Babel 8 開始,"automatic"
會(huì)將兩個(gè)插件默認(rèn)集成在 rumtime 中。欲了解更多信息,請(qǐng)查閱 Babel 文檔中的 @babel/plugin-transform-react-jsx[15] 以及 @babel/preset-react[16]。
注意
如果你在使用 JSX 時(shí),使用 React 以外的庫,你可以使用importSource
選項(xiàng)[17]從該庫中引入 — 前提是它提供了必要的入口?;蛘吣憧梢岳^續(xù)使用經(jīng)典的轉(zhuǎn)換,它會(huì)繼續(xù)被支持。
ESLint
如果你正在使用 eslint-plugin-react[18],其中的 react/jsx-uses-react
和 react/react-in-jsx-scope
規(guī)則將不再需要,可以關(guān)閉它們或者刪除。
{
// ...
"rules": {
// ...
"react/jsx-uses-react": "off",
"react/react-in-jsx-scope": "off"
}
}
TypeScript
TypeScript 將在 v4.1 beta[19] 版本中支持新的 JSX 轉(zhuǎn)換。
Flow
Flow 將在 v0.126.0[20] 中支持新的 JSX 轉(zhuǎn)換。
移除未使用的 React 引入
因?yàn)樾碌?JSX 轉(zhuǎn)換會(huì)自動(dòng)引入必要的 react/jsx-runtime
函數(shù),因此當(dāng)你使用 JSX 時(shí),將無需再引入 React。將可能會(huì)導(dǎo)致你代碼中有未使用到的 React 引入。保留它們也無傷大雅,但如果你想刪除它們,我們建議運(yùn)行 “codemod”[21] 腳本來自動(dòng)刪除它們:
cd your_project
npx react-codemod update-react-imports
注意:
如果你在運(yùn)行 codemod 時(shí)出現(xiàn)錯(cuò)誤,請(qǐng)嘗試使用npx react-codemod update-react-imports
選擇不同的 JavaScript 環(huán)境。尤其是選擇 "JavaScript with Flow" 時(shí),即使你未使用 Flow,也可以選擇它,因?yàn)樗?JavaScript 支持更新的語法。如果遇到問題,請(qǐng)告知我們[22]。
請(qǐng)注意,codemod 的輸出可能與你的代碼風(fēng)格并不匹配,因此你可能需要在 codemod 完成后執(zhí)行 Prettier[23] 以保證格式一致。
運(yùn)行 codemod 會(huì)執(zhí)行如下操作:
- 升級(jí)到新的 JSX 轉(zhuǎn)換,刪除所有未使用的 React 引入。
- 改變所有 React 的默認(rèn)引入將被改為解構(gòu)命名引入(例如,
import React from "react"
會(huì)變成import { useState } from "react"
),這將成為未來開發(fā)的首選風(fēng)格。codemod 不會(huì) 影響現(xiàn)有的命名空間引入方式(即import * as React from "react"
),這也是一種有效的風(fēng)格,默認(rèn)的引入將在 React 17 中繼續(xù)工作,但從長遠(yuǎn)來看,我們建議盡量不使用它們。
示例:
import React from 'react';
function App() {
return <h1>Hello World</h1>;
}
將被替換為
function App() {
return <h1>Hello World</h1>;
}
如果你使用了 React 的其他導(dǎo)出 — 比如 Hook,那么 codemod 將把它們轉(zhuǎn)換為具名導(dǎo)入。
示例:
import React from 'react';
function App() {
const [text, useText] = React.useState('Hello World');
return <h1>{text}</h1>;
}
會(huì)被替換為
import { useState } from 'react';
function App() {
const [text, useText] = useState('Hello World');
return <h1>{text}</h1>;
}
除了清理未使用的引入外,此工具還可幫你為未來 React 主要版本(不是 React 17 版本)做鋪墊,該版本將支持 ES 模塊,并且沒有默認(rèn)導(dǎo)出。
鳴謝
我們要感謝 Babel,TypeScript,Create React App,Next.js,Gatsby,ESLint 以及 Flow 的主要維護(hù)者為新 JSX 轉(zhuǎn)換提供的實(shí)現(xiàn)和整合。我們還要感謝 React 社區(qū)對(duì)相關(guān) RFC[24] 提供的反饋和討論。
參考資料
[1]并未包含新特性: /blog/2020/08/10/react-v17-rc.html
[2]Babel: https://babeljs.io/blog/2020/03/16/7.9.0#a-new-jsx-transform-11154httpsgithubcombabelbabelpull11154
[3]React 17 的 RC 版本: /blog/2020/08/10/react-v17-rc.html
[4]下方: #how-to-upgrade-to-the-new-jsx-transform
[5]性能優(yōu)化和簡化: https://github.com/reactjs/rfcs/blob/createlement-rfc/text/0000-create-element-changes.md#motivation
[7]React 17 的 RC 版本: /blog/2020/08/10/react-v17-rc.html
[8]我們準(zhǔn)備了一個(gè)自動(dòng)腳本: #removing-unused-react-imports
[9]對(duì)其做兼容支持: https://github.com/facebook/create-react-app/pull/9645
[10]即將發(fā)布的 v4.0 版本: https://gist.github.com/iansu/282dbe3d722bd7231fa3224c0f403fa1
[11]v9.5.3: https://github.com/vercel/next.js/releases/tag/v9.5.3
[12]v2.24.5: https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/CHANGELOG.md#22452-2020-08-28
[13]Gatsby 中遇到 error: https://github.com/gatsbyjs/gatsby/issues/26979
[14]v7.9.0: https://babeljs.io/blog/2020/03/16/7.9.0
[15]@babel/plugin-transform-react-jsx: https://babeljs.io/docs/en/babel-plugin-transform-react-jsx
[16]@babel/preset-react: https://babeljs.io/docs/en/babel-preset-react
[17]importSource
選項(xiàng): https://babeljs.io/docs/en/babel-preset-react#importsource
[18]eslint-plugin-react: https://github.com/yannickcr/eslint-plugin-react
[19]v4.1 beta: https://devblogs.microsoft.com/typescript/announcing-typescript-4-1-beta/#jsx-factories
[20]v0.126.0: https://github.com/facebook/flow/releases/tag/v0.126.0
[21]“codemod”: medium.com/@cpojer/effective-javascript-codemods-5a6686bb46fb
[22]告知我們: https://github.com/reactjs/react-codemod/issues
[23]Prettier: https://prettier.io/
[24]RFC: https://github.com/reactjs/rfcs/pull/107
以上就是W3Cschool編程獅
關(guān)于React 17.0.0-rc.2 版本正式發(fā)布,引入了全新的 JSX 轉(zhuǎn)換的相關(guān)介紹了,希望對(duì)大家有所幫助。