大家好,我是Kason。不知道大家在用React开发的时候有没有注意到react和react-dom这两个包里面有一个很奇怪的属性。否则你将被解雇。为什么会有这样的唬人属性呢?让我们今天谈谈。欢迎加入人类优质前端框架群,放飞React项目架构。我们习惯在项目中使用如下语句引入Hook:import{useState}from'react';这是否意味着所有Hooks的具体实现都在react包中??其实并不是。所有Hooks的具体实现都在ReactFiberHooks.new.js方法中,该方法来自于react-reconciler包。那为什么我们的项目中一直没有主动引入这个包呢?因为react-reconciler中用到的部分被打包到了react-dom中。简单来说,React为了实现跨平台渲染,采用了主模块+渲染器的模式。主要模块是react包,提供了所有常用的方法。渲染器根据宿主环境不同,例如:浏览器环境使用ReactDOM/客户端渲染器SSR使用ReactDOM/服务端渲染器原生环境使用ReactNative渲染器除了宿主环境相关的代码,渲染器还有很多通用逻辑(比如作为Diff算法)。因此,可以认为react-dom是由以下多个包中用到的部分进行封装的:shared,一个包react-reconciler,存放常用方法,提供update相关功能包括Hooks实现,Diff算法,优先级调度,etc.react-dom提供宿主环境方法,如DOMadd/move/delete/modify等封装。这就是为什么主机环境差异很大,但它们可以通过执行useState和触发视图更新来更改状态。原因是Hooks的实现和宿主环境中操作view的方法被打包到同一个包中。既然Hooks的实现是被打包进react-dom(或者宿主环境对应的其他包),那么最终使用的时候如何从react中导出呢?像这样://而不是从'react-dom'导入{useState}from'react';这使用开头提到的变量__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED。内部结构可以认为,当React团队要在react与宿主环境对应包之间共享数据时,会保存在这个神秘的内部变量中。比如上面提到的Hook的具体实现。再比如react和react-dom中都使用了object.assign方法的polyfill,但是如果分别导入两个包,然后分别打包,那么polyfill的代码会在react和react-dom两个包中重复出现反应-dom中间。为了减少重复代码,react会引入object.assign方法的polyfill,然后保存在一个神秘的内部变量中。react是react-dom的peerDependencies。项目中引入这两个包时,react-dom内部使用的object.assign实际上来自react://react-dompackageinternalconstreact=require('react');const{assign}=react.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;常见问题解答了解了神秘内部变量的作用之后,让我们来看看这种实现会导致的问题。假设我们有两个项目:组件库项目A,负责开发组件业务项目B,依赖AB安装依赖后,A会出现在B的node_modules中。为了调试方便,我们使用npmlink函数将B中的依赖A从B的node_modules中的A改为组件库项目A。npmlink之后,B中业务代码使用的useState来自B的node_modules中的react。B中引入的组件库A的组件中使用的useState来自于A的node_modules中的react。不同的react对应不同的__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,最终对应不同的react-dom。这将导致错误。解决方法是在项目中给react加一个别名(alias),这样项目中所有用到react的地方都指向同一个react。总结这篇文章我们了解到神秘的内部变量__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED在react和react-dom中的作用。他能够在这两个包之间传递共享数据。需要注意的一点是,如果你也想通过这种方式在两个包之间共享数据,你需要将一个包设置为另一个包的peerDependencies。否则,打包时,共享数据在两个包中只会存在一份。
