,大家好,我卡颂。,逞着对React内部运行流程还记得住,业余时间尝试复刻一个React —— big-react[1]。,即然是复刻一个React,那肯定得跑通部分官方的测试用例。,在跑一个用例时遇到个很有意思的问题,以下是排查过程。,以下是这个用例的内容:,他测试的是在「不支持Symbol的环境」,jsx的内部属性$$typeof是否正确。,我们知道,jsx仅仅是JS的语法糖,在编译时会被编译成函数调用,比如:,在React.createElement(或jsxRuntime.jsx)方法的实现中,最终会返回如下数据结构:,其中$$typeof属性用于区分「jsx对象的类型」,比如REACT_ELEMENT_TYPE代表这个jsx对象是一个React Element。,在支持Symbol的环境,$$typeof对应一个唯一的symbol。在不支持的环境,对应一个16进制数字。,比如REACT_ELEMENT_TYPE的定义如下:,回到我们的测试用例,他的测试意图就很明显了:在不支持Symbol的环境,「div对应jsx对象」的$$typeof属性应该返回数字0xeac7。,那么如何制造一个「不支持Symbol的环境」呢?,很简单,在所有用例执行前的beforeEach钩子函数(jest提供的)中将global.Symbol置为undefined:,当引入react、react-dom时,其内部执行时global.Symbol === undefined。,这就模拟了「不支持Symbol的环境」。,但是这个用例却挂了:,
,上述代码应该是没问题的,毕竟是React官方会跑的用例。那么问题出在哪儿呢?,在React17发布时,带来了全新的 JSX 转换[2]。,在17之前,jsx会编译为React.createElement,17之后会编译为jsxRuntime.jsx。,同时会在模块顶部引入如下语句:,上述被引入的语句的执行先于下述语句:,所以在语句执行时,环境中还存在global.Symbol,就造成开篇提到的问题。,那为什么React官方跑用例时没有问题呢?,答案是:React跑用例时会将jsx编译为React.createElement。,这样不会在模块顶部插入新的引入语句。,当引入React时,环境中已经不存在global.Symbol了:,由于编译在内存中进行,不太好排查编译后代码。所以如果对React各方面特性了解不深的话,这个问题真不太好排查。,当前big-react[3]代码量还比较少。,[1]big-react:https://github.com/BetaSu/big-react。,[2]全新的 JSX 转换:https://zh-hans.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html。,[3]big-react:https://github.com/BetaSu/big-react。
© 版权声明
文章版权归作者所有,未经允许请勿转载。