天天看點

一次性清除“知乎”所有DIV

學習本文的前置知識:

  • ​React​

    ​​每次更新都會從​

    ​rootFiber​

    ​​(根​

    ​Fiber​

    ​節點)向下深度優先周遊
  • ​JSX​

    ​​在編譯時會變為​

    ​React.createElement​

    ​​,在元件​

    ​render​

    ​時會調用該方法。

30秒速答:

知乎首頁是​

​React​

​​寫的,我們可以覆寫​

​React.createElement​

​​方法,在運作時将所有​

​div​

​​節點渲染為​

​React.Fragment​

​。

這樣就能清除所有​

​div​

​。

讓我們來愉快的改造​

​知乎​

​吧。

拿到React對象

這時候遇到了第一個問題:​

​知乎​

​​沒把​

​React​

​​暴露到全局(廢話,當然不會),怎麼擷取​

​React​

​對象呢?

一次性清除“知乎”所有DIV

好在當我們使用​

​React Dev Tools​

​​時,​

​Dev Tools​

​​會向頁面注入全局變量​

​__REACT_DEVTOOLS_GLOBAL_HOOK__​

​。

這個變量是連接配接​

​React​

​​與​

​Dev Tools​

​的橋梁。

一次性清除“知乎”所有DIV

其中​

​renderers​

​​屬性指頁面使用的​

​渲染器​

​。

​React​

​​源碼架構劃分為​

​排程器 - 協調器 - 渲染器​

​。

對于不同的宿主環境,使用不同的​

​渲染器​

​。

  • 對于​

    ​web​

    ​,使用​

    ​ReactDOM渲染器​

  • 對于​

    ​用戶端​

    ​,使用​

    ​React-Native渲染器​

在​

​renderers​

​​屬性中,我們發現一個方法​

​findFiberByHostInstance​

​:

一次性清除“知乎”所有DIV

方法名居然出現了​

​Fiber​

​字樣。

​Fiber節點​

​​是​

​React​

​​的最小可排程單元,可以了解為​

​虛拟DOM節點​

​。

那​

​findFiberByHostInstance​

​​方法所在檔案一定有​

​React​

​相關定義。我們右鍵跳轉到定義函數的檔案,

一次性清除“知乎”所有DIV

在檔案内搜​

​.createElement​

一次性清除“知乎”所有DIV

果然讓我們找到了。打上斷點,重新整理頁面試試

一次性清除“知乎”所有DIV

果然進來了,事情越發有趣了。

看看​

​o.a​

​​包含的屬性,​

​Children​

​​、​

​createElement​

​......

看來這就是​

​React​

​對象了。

一次性清除“知乎”所有DIV

我們将來之不易的​

​React​

​​對象儲存在​

​window​

​​,順便把​

​React.createElement​

​也儲存一份。

一次性清除“知乎”所有DIV

現在放開斷點,​

​window.React​

​已經指向知乎首頁内部使用的React啦。

一次性清除“知乎”所有DIV

修改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

​div​

​都消失啦,終于恢複了往日的清爽界面(大誤)

理論上我們可以用這個方法将任何​

​React​

​應用改造成任何樣子。