react
第二课:实现JSX

本节课代码地址 (opens in a new tab)

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 pluginnode包:

pnpm i -D -w rimraf rollup-plugin-generate-package-json rollup-plugin-typescript2 @rollup/plugin-commonjs

调试打包结果

pnpm link文档 (opens in a new tab)

pnpm_link_react.png

这种方式的优点:可以模拟实际项目引用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转换的因素

有些同学发现:在源码中jsxkey属性是第三个传参,但视频中实现createElement时是从config参数中获取。造成这些区别的原因如下:

  • 在测试用例(后续课程会实现)中使用的是createElement,在打包后代码中使用的是jsx。虽然这两者最后都是生成ReactElement,但他们的传参是不同的。

jsx的参数如下:

  • 对于开发环境,jsxDEV参数依次为typepropskeysourceself。其中后两者为开发环境用于调试的参数

  • 对于生产环境,jsx参数依次为typepropskey

key作为一个单独的传参,为了体现了他的特殊性(与节点稳定相关),所以与其他props区分开。

createElement的参数依次为typeprops...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")
);
 

createElementjsx之所以会有这些区别更详细的原因可以参考createlement-rfc (opens in a new tab)

所以结论是,由于以下情况传参都有区别:

  • createElementjsx

  • jsx在生产环境与开发环境

所以我们起码需要实现createElementjsx这2个API,如果为了更高的源码还原度,我们甚至应该实现3个版本(createElement与生产、开发环境下的jsx),以应对使用不同API的场景(比如对于部分测试用例,使用的是createElement

本节课程没有区分jsxcreateElement,这是不严谨的。

rollup 的 output 中 name 字段作用

name 主要用于 UMD 和 IIFE 模块格式,使用 es 则不需要设置

当使用这两种模块格式时,Rollup 需要知道将哪个名称(也就是 name 选项的值)添加到全局命名空间(即,window 对象)。

name:react 其实 打包之后会在全局对象中出现 window.react 属性,可以直接访问。

详见rollup文档#outputname (opens in a new tab)