思考这个问题的起因是因为公司项目的需求使用esbuild-loader替换babel-loader后,而我们大家都知道我们在rc中写的jsx或者tsx代码,首先都会经过babel转译(rc16版本与17版本转译后会有所不同,在下面会提及),而这个babel转译的过程,是在哪里发生的,或者说这个babel是在哪里的babel,是webpack中配置的babel还是rc本身源码中内置类似babel功能的函数(猜想)?当通过webpack启动项目,jsx代码的走向是怎么走的?(估计大家会觉得这个问题是我想的太多~~~)
猜想
在使用esbuild-loader替换babel-loader之后,项目的启动和打包速度都会有所提升,所以初步猜想,rc中转译jsx代码是webpack中的babel,但是思前想后总觉得这个结论过于随意,于是想到了另一个办法来验证。验证之前,先要了解babel对于rc新旧版本中jsx转换的不同。rc官网中有提及:
https://zh-hans.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html
新旧jsx转换对比
假设源代码如下:
import React from 'react';
function App() {
return <h1>Hello World</h1>;
}
旧版本中转换如下:
import React from 'react';
function App() {
return React.createElement('h1', null, 'Hello world');
}
新版本中转换如下:
// 由编译器引入(禁止自己引入!)
var _jsxRuntime = require("react/jsx-runtime");
function App() {
return /*#__PURE__*/(0, _jsxRuntime.jsx)("h1", {
children: "Hello World"
});
}
可以看出,新版本转换后的代码已经不是使用React.createElement构建dom了,所以在rc17中,源代码无需特地引入 React 即可使用 JSX 了!(但仍需引入 React,以便使用 React 提供的 Hook 或其他导出。)
同时在官网中有提及:
目前,旧的转换的默认选项为
{"runtime": "classic"}。如需启用新的转换,你可以使用{"runtime": "automatic"}作为@babel/plugin-transform-react-jsx或@babel/preset-react的选项
于是我们可以在rc中webpack.config.js中找到如下代码:
{
test: /.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve("babel-loader"),
options: {
customize: require.resolve(
"babel-preset-react-app/webpack-overrides"
),
presets: [
[
require.resolve("babel-preset-react-app"),
{
runtime: hasJsxRuntime ? "automatic" : "classic",
},
],
],
// ....
}
可以看到是通过hasJsxRuntime这个立即执行函数来控制使用新旧转换的,接下来找到这个函数:
const hasJsxRuntime = (() => {
if (process.env.DISABLE_NEW_JSX_TRANSFORM === "true") {
return false;
}
try {
require.resolve("react/jsx-runtime");
return true;
} catch (e) {
return false;
}
})();
验证猜想
既然我们已经找到了开关,就可以开始验证猜想了,如果转换的babel使用的是webpack中配置的babel,那么我们切换新旧版本的开关一定对代码有所影响。
- 不改变源代码,开启旧版本的转换 -- 猜想: 页面不报错,正常运行
- 将源代码中引入react部分删除,开启旧版本的转换 -- 猜想: 页面报错 React is not defined
- 开启新版本的转换 -- 猜想: 页面不报错,正常运行
如何切换新旧版本开关,这里直接简单粗暴,直接控制hasJsxRuntime函数的return true或者false,结果也正如猜想一样,进行到第二步时候,页面报错如下:
进行到第三步,开启新版本的转换,页面不报错,正常运行。
总结
通过以上,可以看出的确是webpack中的babel配置项在作用jsx代码的转换;在rc17版本中,项目启动,首先经过webpack中的babel转译jsx代码,随后将由rc自己处理,最终渲染dom节点到界面,这个处理的过程在后面的文章中会逐步介绍。
babel转译链接:有兴趣可以自己尝试,通过配置左侧options--React Runtime来实现新旧版本的转译