学习本文的前置知识:
React
每次更新都会从rootFiber
(根Fiber
节点)向下深度优先遍历JSX
在编译时会变为React.createElement
,在组件render
时会调用该方法。
30秒速答:
知乎首页是React
写的,我们可以覆写React.createElement
方法,在运行时将所有div
节点渲染为React.Fragment
。
这样就能清除所有div
。
让我们来愉快的改造知乎
吧。
拿到React对象
这时候遇到了第一个问题:知乎
没把React
暴露到全局(废话,当然不会),怎么获取React
对象呢?
好在当我们使用React Dev Tools
时,Dev Tools
会向页面注入全局变量__REACT_DEVTOOLS_GLOBAL_HOOK__
。
这个变量是连接React
与Dev Tools
的桥梁。
其中renderers
属性指页面使用的渲染器
。
React
源码架构划分为调度器 - 协调器 - 渲染器
。
对于不同的宿主环境,使用不同的渲染器
。
-
对于
web
,使用ReactDOM渲染器
-
对于
客户端
,使用React-Native渲染器
在renderers
属性中,我们发现一个方法findFiberByHostInstance
:
方法名居然出现了Fiber
字样。
Fiber节点
是React
的最小可调度单元,可以理解为虚拟DOM节点
。
那findFiberByHostInstance
方法所在文件一定有React
相关定义。我们右键跳转到定义函数的文件,
在文件内搜.createElement
果然让我们找到了。打上断点,刷新页面试试
果然进来了,事情越发有趣了。
看看o.a
包含的属性,Children
、createElement
......
看来这就是React
对象了。
我们将来之不易的React
对象保存在window
,顺便把React.createElement
也保存一份。
现在放开断点,window.React
已经指向知乎首页内部使用的React啦。
修改React.createElement
React.createElement
方法第一个参数为type
。
在源码中,我们找到React.Fragment
对应的type
。
if (typeof Symbol === 'function' && Symbol.for) { const symbolFor = Symbol.for; REACT_ELEMENT_TYPE = symbolFor('react.element'); REACT_PORTAL_TYPE = symbolFor('react.portal'); REACT_FRAGMENT_TYPE = symbolFor('react.fragment'); REACT_STRICT_MODE_TYPE = symbolFor('react.strict_mode'); REACT_PROFILER_TYPE = symbolFor('react.profiler'); REACT_PROVIDER_TYPE = symbolFor('react.provider'); // ... }
接下来,修改全局变量,将所有div
变为Fragment
。
React.createElement = (type, ...args) => { if (type === 'div') { type = Symbol.for('react.fragment'); } // originCreateElement是原始React.createElement return originCreateElement(type, ...args); }
让我们滚动页面,触发随便啥组件的setState
。
接下来,就是见证奇迹的时刻。。。
div
都消失啦,终于恢复了往日的清爽界面(大误)
理论上我们可以用这个方法将任何React
应用改造成任何样子。
转自https://mp.weixin.qq.com/s?__biz=MzkzMjIxNTcyMA==&mid=2247485234&idx=1&sn=5962c47e3513e9cc4f6d8872df50bdbb&source=41#wechat_redirect