这是个无聊的问题吗当提出这个问题时,我的第一反应是——我还真是无聊 ?♂?
转念一想,em~,好像事情没有这么简单如果直接选择所有div再遍历删除的话,div的子孙节点也被删除了。有什么办法能在保留整棵DOM树层级关系的前提下,只删除div节点呢?我陷入了沉思。。。。。
知乎是React写的,React用JSX来表示页面层次结构,JSX在编译时会被babel转换为React.createElement。在代码运行时,React获取的其实是React.createElement()的返回值。办法来了!
我们只需要覆写一下React.createElement方法,当遇到type === 'div',我们将type修改为React.Fragment,即我们把所有div节点都变成Fragment,那不就能在保持树的层级关系的同时去掉div了?让我们开始吧!!如何拿到React对象这时候遇到了第一个问题:知乎没把React暴露到全局(废话,当然不会 ),怎么获取React对象呢?
好在当我们使用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方法我们知道,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都消失了,终于恢复了往日的清爽界面(大误)
总结通过这篇无聊的文章,我们认识到:
- React每次触发setState更新,都会重新遍历整棵Fiber树。(否则也不会点击一个按钮整个页面的div都消失)
- React.createElement的深度应用。
- 知乎真是有太多div了
PS:如果你用Chrome将被压缩后的代码formatted后打上断点,刷新页面进入断点后浏览器卡死,不要怀疑,这是Chrome的bug,截止版本 81.0.4044.138(正式版本) (64 位)还未修复,建议使用chrome开发者版本 Chrome Canary
作者姓名:卡颂
转发链接:https://juejin.im/post/5ecb2af0