当前位置: 首页 > Web前端 > HTML

在实施React过程中的一次有趣的排错经历

时间:2023-03-28 15:57:27 HTML

大家好,我是Kason。最近有一本关于React的新书交付(预计年底出版),时间比较多。在还记得React内部运行过程的同时,尝试在空闲时间重现一个React——big-react。既然是React的翻版,肯定要跑过一些官方的测试用例。我在运行一个用例的时候遇到了一个很有意思的问题。以下是故障排除过程。欢迎加入人类优质前端框架群。以下是这个用例的内容:it('在没有Symbol的环境中使用回退值',()=>{expect((

).$$typeof).toBe(0xeac7);});他在不支持Symbol的环境下测试jsx的内部属性$$typeof是否正确。我们知道jsx只是JS的语法糖,在编译的时候会被编译成函数调用,比如://编译前
//编译后React17之前React.createElement('div');//编译后React17后,jsxRuntime.jsx('div');在React.createElement(或jsxRuntime.jsx)方法的实现中,最终会返回如下数据结构:constelement:ReactElement={$$typeof:REACT_ELEMENT_TYPE,type,key,ref,props};$$typeof属性用来区分jsx对象的类型,比如REACT_ELEMENT_TYPE表示jsx对象是一个ReactElement。在支持Symbol的环境中,$$typeof对应一个唯一的符号。在不受支持的环境中,对应于十六进制数。例如REACT_ELEMENT_TYPE的定义如下:constsupportSymbol=typeofSymbol==='function'&&Symbol.for;导出常量REACT_ELEMENT_TYPE=supportSymbol?Symbol.for('react.element'):0xeac7;回到我们的测试用例,他的测试意图很明显:在不支持Symbol的环境下,jsx对象对应的div的$$typeof属性应该返回数字0xeac7。it('在没有Symbol的环境中使用回退值',()=>{expect((
).$$typeof).toBe(0xeac7);});那么如何制作一个不支持Symbol的环境呢?很简单,在所有用例执行前在beforeEach钩子函数(由jest提供)中设置global.Symbol为undefined:beforeEach(()=>{jest.resetModules();originalSymbol=global.Symbol;//制造不支持Symbol环境global.Symbol=undefined;React=require('react');ReactDOM=require('react-dom');ReactTestUtils=require('react-dom/test-utils');});在导入react时,在react-dom中,global.Symbol===undefined在内部执行时。这模拟了一个不支持Symbol的环境。但是这个用例就挂了:上面的代码应该没问题,毕竟是React会跑的官方用例。所以有什么问题?在React17发布时,babel的锅带来了全新的JSX改造。17之前jsx会编译成React.createElement,17之后会编译成jsxRuntime.jsx。同时,模块顶部会引入如下语句:import{jsxas_jsx}from"react/jsx-runtime";import{jsxsas_jsxs}from"react/jsx-runtime";上面引入的语句的执行先于下面的语句:originalSymbol=global.Symbol;global.Symbol=undefined;所以当语句执行时,global.Symbol仍然存在于环境中,这就造成了开头提到的问题。那么为什么React正式运行用例就没有问题呢?答案是:React在运行用例的时候会把jsx编译成React.createElement。这不会在模块顶部插入新的导入语句。当引入React时,global.Symbol不再存在于环境中:originalSymbol=global.Symbol;global.Symbol=undefined;反应=要求(“反应”);ReactDOM=require('react-dom');ReactTestUtils=require('react-dom/test-utils');总结由于编译是在内存中进行的,所以不容易检查编译后的代码。所以如果你对React的方方面面都没有深入的了解,这个问题确实不好排查。目前big-react的代码量还比较少,有兴趣从0实现React的朋友可以关注一下,给个star~