Skip to content

实现JSX

本节课代码地址(请参考课程获取) React项目结构: react (宿主环境无关的公用方法) react-reconciler (协调器的实现,宿主环境无关) 各种宿主环境的包 shared (公用辅助方法,宿主环境无关) 本节课将实现的JSX转换属于react 包。

JSX转换是什么

JSX转换 playground

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

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

调试打包结果 pnpm link文档 这种方式的优点:可以模拟实际项目引用React 的情况 缺点:对于我们当前开发big-react 来说,略显繁琐。对于开发过程,更期望的是热更新效 果。 补充知识

@rollup/plugin-commonjs 插件的作用

rollup 原生支持ESM 格式,所以对于CJS 格式的包,我们需要先将它用该插件转为ESM 格式。 这里使用该插件是因为他是rollup 中最常见的插件,加上有备无患。

如果报如下错误:

ERROR  Unable to find the global bin directory

结局方案参考解决pnpm 升级之后全局安装出现异常问题

影响JSX 转换的因素

有些同学发现:在源码中jsxkey 属性是第三个传参,但视频中实现createElement 时 是从config 参数中获取。造成这些区别的原因如下: 在测试用例(后续课程会实现)中使用的是createElement ,在打包后代码中使用的 是jsx 。虽然这两者最后都是生成ReactElement ,但他们的传参是不同的。 jsx 的参数如下: 对于开发环境,jsxDEV 参数依次为typepropskeysourceself 。其 中后两者为开发环境用于调试的参数 对于生产环境,jsx 参数依次为typepropskeykey 作为一个单独的传参,为了体现了他的特殊性(与节点稳定相关),所以与其他props 区分开。 createElement 的参数依次为typeprops...children 。其中children 及后续其 他传参经过转换都会作为children 属性,比如对于:

jsx
<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 所以结论是,由于以下情况传参都有区别:

createElement 与jsx

jsx 在生产环境与开发环境 所以我们起码需要实现createElementjsx 这2个API ,如果为了更高的源码还原度, 我们甚至应该实现3个版本(createElement 与生产、开发环境下的jsx ),以应对使用不 同API 的场景(比如对于部分测试用例,使用的是createElement ) 本节课程没有区分jsxcreateElement ,这是不严谨的。这么做可能造成的后果之一是 「由于key传参位置不同,导致在某些情况下key丢失」。比如应该用jsx 的场景(key 是 第三个参数),使用了createElementkey 在第二个参数props 中),就会导致key 被 当成createElementchildren prop ,进而导致key 丢失。解决办法如下,学员可自行 更新到代码中: fix: jsx与createElement实现一致导致key丢失的问题 纠错

  1. jsx-runtimeoutput 中输出格式参数名应为format 而不是formate
  2. reactoutputname 字段的值应为react 而不是index.js 对于umd 的格式,如果是以esm 引入,则两者没有区别。但如果在浏览器引入,前者会挂 载window.react ,而后者会挂载window.index 。 详见rollup文档#outputname

用心学习,用代码说话 💻