close

不知道大家在用React開發時,有沒有注意到react與react-dom這兩個包中有個很奇葩的屬性__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:

直譯過來就是「內部神秘屬性,不要亂用!否則你會被炒魷魚」。

為什麼會有個這麼唬人的屬性?今天我們來聊聊。

React項目架構

我們在項目中習慣使用如下語句引入Hook:

import{useState}from'react';

這是不是意味着所有Hook的具體實現都在react這個包中?實際不是的。

所有Hook的具體實現在ReactFiberHooks.new.js方法中,該方法來自於react-reconciler這個包。

那為什麼我們項目中從來沒有主動引入過這個包呢?因為react-reconciler中被使用的部分,被打包進react-dom中了。

簡單來說,React為了實現跨平台渲染,採用的是「一個主模塊」 + 「一個渲染器」的模式。

其中「主模塊」就是react包,他提供了所有通用方法。

「渲染器」針對宿主環境不同而不同,比如:

瀏覽器環境使用ReactDOM/client渲染器

SSR使用ReactDOM/server渲染器

Native環境使用ReactNative渲染器

渲染器除了「宿主環境相關的代碼」外,還有大量通用邏輯(比如Diff算法)。

所以可以認為,react-dom是由如下多個包中「被使用的部分」打包而成:

shared,一個存放通用方法的包

react-reconciler,提供包括Hooks的實現、Diff算法、優先級調度等更新相關功能

react-dom,提供宿主環境方法,比如「DOM的增/移動/刪/改」

等等其他包

這也是為什麼宿主環境千差萬別,但都能通過執行useState改變狀態,觸發視圖更新。

原因在於 —— 「Hooks的實現」與「宿主環境操作視圖的方法」被打包進了同一個包中。

既然「Hooks的實現」被打包進react-dom(或其他宿主環境對應的包)中,那如何做到最終使用時是從react中導出的呢?就像這樣:

//而不是from'react-dom'import{useState}from'react';

這就用到了開篇提到的變量__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED。

內部結構

可以認為,當React團隊希望在react與「宿主環境對應的包」之間共享數據時,就會把他保存在這個神秘的內部變量中。

比如上文提到的,「Hook的具體實現」。

再比如,object.assign方法的polyfill,在react與react-dom中都會用到,但如果兩個包中分別引入,再分別打包,那麼polyfill的代碼會重複出現在react與react-dom兩個包中。

為了減少重複代碼,react會引入object.assign方法的polyfill,再將它保存在神秘的內部變量中。

react作為react-dom的peerDependencies,當項目中引入這兩個包後,react-dom內部使用的object.assign實際來自react:

//react-dom包內部constreact=require('react');const{assign}=react.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;常見問題

了解了神秘的內部變量的作用,我們再來看看這種實現會造成的問題。

假設我們有2個項目:

組件庫項目A,負責開發組件

業務項目B,依賴A

B安裝依賴後,A會出現在B的node_modules中。

為了調試方便,我們用npm link功能將B中依賴的A由「B的node_modules中的A」改為「組件庫項目A」,

當npm link後,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。

總結

本文我們了解了react與react-dom中神秘的內部變量__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED的作用。

他能夠在這兩個包之間傳遞共享的數據。

需要注意的一點是,如果你也想用這種方式在兩個包之間共享數據,需要將其中一個包設為另一個包的peerDependencies。

否則,在打包時,「被共享的數據」只會在兩個包中分別存在一份。

彥祖,亦菲,點個「在看」吧
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()