当提出这个问题时,个人第一反应是——我还真是无聊 🤷♂️react
转念一想,em~,好像事情没有这么简单🤔git
若是直接选择全部div
再遍历删除的话,div
的子孙节点也被删除了。有什么办法能在保留整棵DOM树层级关系的前提下,只删除div
节点呢?github
我陷入了沉思。。。。。web
知乎是React写的,React用JSX来表示页面层次结构,JSX在编译时会被babel转换为React.createElement
。面试
在代码运行时,React获取的实际上是React.createElement()
的返回值。chrome
办法来了!浏览器
咱们只须要覆写一下React.createElement
方法,当遇到type === 'div'
,咱们将type
修改成React.Fragment
,即咱们把全部div
节点都变成Fragment
,那不就能在保持树的层级关系的同时去掉div
了?babel
让咱们开始吧!!编辑器
这时候遇到了第一个问题:知乎没把React暴露到全局(废话,固然不会😠),怎么获取React对象呢? ide
好在当咱们使用React Dev Tools
时,Dev Tools
会向页面注入全局变量__REACT_DEVTOOLS_GLOBAL_HOOK__
,这个变量会成为连接React
与Dev Tools
的桥梁。
其中的renderers
属性指页面使用的渲染器,在网页端就是咱们熟悉的React-DOM
(在客户端固然就是React-Native
啦)
咱们发现一个方法findFiberByHostInstance
方法名竟然出现了Fiber
字样!!
咱们知道,Fiber
是React的最小可调度单元(别问为何,问就是安利我写的React源码揭秘系列)
那findFiberByHostInstance
方法所在文件必定有React相关定义。咱们右键跳转到定义函数的文件,
在文件内搜.createElement
果真让咱们找到了。打上断点,刷新页面试试~
果真进来了,事情愈加有趣了 😊😊😊
看看o.a
是什么,em~~一个对象,内部有Children
、createElement
。。。。。。
看来这就是React对象了
赶紧把来之不易的React对象保存在全局,顺便把React.createElement也保存一份。
如今放开断点,window.React
已经指向知乎首页内部使用的React啦。
咱们知道,React.createElement方法第一个参数为type
(别问为何,问又是一波安利React源码揭秘系列),咱们再到React文档中找来React.Fragment
的定义。
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'); // ... } 复制代码
接下来,修改全局变量
React.createElement = (type, ...args) => {
if (type === 'div') { type = Symbol.for('react.fragment'); } // originCreateElement才是正经的React.createElement return originCreateElement(type, ...args); } 复制代码
让咱们康康此时的页面结构
ok~~~ 满屏的div套div(嫌弃脸),接着咱们轻轻的点一下关注按钮,触发随便啥组件的setState
。
接下来,就是见证奇迹的时刻。。。
除了根节点,其余div
都消失啦,终于恢复了往日的清爽界面(大误)
经过这篇无聊的文章,咱们认识到:
setState
更新,都会从新遍历整棵Fiber树。(不然也不会点击一个按钮整个页面的
div
都消失)
div
了
PS:若是你用Chrome将被压缩后的代码formatted
后打上断点,刷新页面进入断点后浏览器卡死,不要怀疑,这是Chrome的bug,截止版本 81.0.4044.138(正式版本) (64 位)
还未修复,建议使用chrome开发者版本 Chrome Canary