React项目结构:
-
react
(宿主环境无关的公用方法) -
react-reconciler
(协调器的实现,宿主环境无关) -
各种宿主环境的包
-
shared
(公用辅助方法,宿主环境无关)
本节课将实现的JSX转换属于react
包。
JSX转换是什么
JSX转换 playground (opens in a new tab)
import { jsx as _jsx } from "react/jsx-runtime";
/*#__PURE__*/_jsx("div", {
children: "123"
});
// 或
/*#__PURE__*/React.createElement("div", null, "123");
包括两部分:
-
编译时
-
运行时:
jsx
方法或React.createElement
方法的实现(包括dev、prod两个环境)
编译时
由babel
编译实现,我们来实现运行时
,工作量包括:
-
实现
jsx
方法 -
实现打包流程
-
实现调试打包结果的环境
实现jsx方法
包括:
-
jsxDEV方法
(dev环境) -
jsx
方法(prod环境) -
React.createElement
方法
实现打包流程
对应上述两3方法,打包对应文件:
-
react/jsx-dev-runtime.js
(dev环境) -
react/jsx-rumtime.js
(prod环境) -
React
打包流程中需要安装的rollup plugin
与node
包:
pnpm i -D -w rimraf rollup-plugin-generate-package-json rollup-plugin-typescript2 @rollup/plugin-commonjs
调试打包结果
pnpm link文档 (opens in a new tab)
这种方式的优点:可以模拟实际项目引用React
的情况
缺点:对于我们当前开发big-react
来说,略显繁琐。对于开发过程,更期望的是热更新效果。
补充知识
@rollup/plugin-commonjs
插件的作用
rollup
原生支持ESM
格式,所以对于CJS
格式的包,我们需要先将它用该插件转为ESM
格式。
这里使用该插件是因为他是rollup
中最常见的插件,加上有备无患。
执行pnpm link --global
时报错
如果报如下错误:
ERROR Unable to find the global bin directory
结局方案参考解决pnpm 升级之后全局安装出现异常问题 (opens in a new tab)
影响JSX
转换的因素
有些同学发现:在源码中jsx
的key
属性是第三个传参,但视频中实现createElement
时是从config
参数中获取。造成这些区别的原因如下:
- 在测试用例(后续课程会实现)中使用的是
createElement
,在打包后代码中使用的是jsx
。虽然这两者最后都是生成ReactElement
,但他们的传参是不同的。
jsx
的参数如下:
-
对于开发环境,
jsxDEV
参数依次为type
、props
、key
、source
、self
。其中后两者为开发环境用于调试的参数 -
对于生产环境,
jsx
参数依次为type
、props
、key
key
作为一个单独的传参,为了体现了他的特殊性(与节点稳定相关),所以与其他props
区分开。
createElement
的参数依次为type
、props
、...children
。其中children
及后续其他传参经过转换都会作为children
属性,比如对于:
<ul>
<li>1</li>
</ul>
<ul>
<li>1</li>
<li>2</li>
</ul>
转换结果为:
React.createElement("ul", null,
React.createElement("li", null, "1")
);
React.createElement("ul", null,
React.createElement("li", null, "1"),
React.createElement("li", null, "2")
);
createElement
与jsx
之所以会有这些区别更详细的原因可以参考createlement-rfc (opens in a new tab)
所以结论是,由于以下情况传参都有区别:
-
createElement
与jsx
-
jsx
在生产环境与开发环境
所以我们起码需要实现createElement
与jsx
这2个API
,如果为了更高的源码还原度,我们甚至应该实现3个版本(createElement
与生产、开发环境下的jsx
),以应对使用不同API
的场景(比如对于部分测试用例,使用的是createElement
)
本节课程没有区分jsx
与createElement
,这是不严谨的。
rollup 的 output 中 name 字段作用
name 主要用于 UMD 和 IIFE 模块格式,使用 es 则不需要设置
当使用这两种模块格式时,Rollup 需要知道将哪个名称(也就是 name 选项的值)添加到全局命名空间(即,window 对象)。
如 name:react
其实 打包之后会在全局对象中出现 window.react
属性,可以直接访问。