探索React原始碼的全域性模組系統

2021-09-19 10:08:44 字數 2804 閱讀 7520

也可以在這裡看:

掃了幾眼react的源**(0.14-stable分支),發現乙個有趣的現象,比如如下這段**:

var reactdom = require('reactdom');

var reactdomserver = require('reactdomserver');

var reactisomorphic = require('reactisomorphic');

var assign = require('object.assign');

var deprecated = require('deprecated');

熟悉 node.js 的 commonjs 模組系統的話,我們知道有如下3種情況:

根據以上規則,例子中的**顯然屬於第三種情況,然而實際上reactdom或者object.assign這幾個模組並不屬於node_modules資料夾,它們其實也存在與本地的源**中,比如對應的object.assign模組實際上位於/src/shared/stubs/object.assign.js

引用 google groups 上乙個回答,這是它們的全域性模組系統。出於好奇,決定探索一番,看看這是如何實現的。

首先的一點是,由於它的模組依賴方式和我們熟悉的方式並不吻合,所以我們需要探索這個部分的工作流,看這個全域性模組系統是如何融入整個開發過程中的。

從源**裡知道到了這部分任務,是定義在gulpfile.js中的react:modules任務:

也就是說,本來這樣的目錄:

- src

- lib

- reactelement.js

- reactdom.js

- index.js

變成了這樣:

- build 

- index.js

- reactelement.js

- reactdom.js

如果index.js中本來有require('reactelement'),最後就被編譯為require('./reactelement')了。

正是有這樣的乙個步驟,讓這個全域性模組系統得以工作,再思考下其中的細節,這個編譯過程需要做哪些東西:

好的,順著這個思路在來看看**,我們發現主要是rewrite-modules這個babel外掛程式來負責這個事情,這是facebook的自定義babel外掛程式,要了解如何編寫乙個自定義babel外掛程式的話,可以參考這篇文件。

而fbjs這個專案在編譯的時候會生成乙個module-map.json的檔案,來表示唯一模組識別符號和正常方式引用模組的識別符號之間的對映,那麼這個檔案是如何生成的呢?

fbjs/scripts/gulp/module-map.js的**來看,是用了@providesmodules來標記模組,比如areequal.js這個檔案的注釋中可以發現:

* @providesmodule areequal
並且有乙個prefix的設定,設定為fbjs/lib/,所以如果我有如下**:

require('areequal')
則會被編譯成:

require('fbjs/lib/areequal')
不過奇怪的是,在react的源**中也可以發現@providesmodules標記,但在 react 源**編譯的工作流中,並沒有發現解析這個標記的邏輯,它的邏輯是:如果模組在 fbjs 的 modulemap 中找不到,則直接加上./的字首,也就是說:

require('reactelement')
直接變成:

require('./reactelement')
我也嘗試修改 react 源**中的@providesmodules,對編譯結果沒有影響。至於這裡為什麼會有兩種不同的邏輯,我也不清楚。

很清楚了,開始的時候也說過了,那個負責編譯源**的 gulp task 中,有扁平化這個源**的目錄結構的任務,那麼所有本地模組,也都可以被正確引用到了。

我還發現乙個工具,就是這個 commoner 了,它可以編譯你的**,解析你注釋中的@providesmodules,輸出乙個扁平化的目錄,檔名為各自的模組識別符號的名字,require()也會被替換成正確的相對路徑,有興趣的話可以了解下這個工具,好像也是 reactjs 這個 organiztion 裡的,不過不知道為什麼不用了,估計是因為要迎合 babel 生態的關係吧,react 的專案中用 babel 外掛程式代替了它。

大致考慮了一下,為什麼fb的團隊會整出這個所謂的『全域性模組系統』,我覺得還是和它巨大的 codebase 是有關的,什麼 react、rn、flow、relay 等等,那麼必然會有一些公共的工具庫,而且像 react 乙個專案本身的 codebase 也很大了,所以要維護各種相對路徑,很吃力,但有利有弊吧:

好處:缺點:

其實還是挺有意思的,在探索的過程也順便了解了babel外掛程式的編寫,過了元旦要開始新的專案了,準備嘗試嘗試,把它加進工作流中去。

react原始碼探索

react核心部分為 1 虛擬dom物件 reactdom.render args,element 這個方法第乙個引數接收三種形式的內容的 第一種 字串 第二種 由createclass建立的物件,使用createelement處理 第三種 直接有createelement建立的物件 這些還未呼叫r...

Byte的原始碼探索

非可變類 final class 實現對比介面 comparable 繼承於數字類 numberbyte min value 最小值 byte max value 最大值 classtype 類型別 byte value 初始值 int size bit位數 int bytes 位元組數static...

MyBatis原始碼探索

每個基於 mybatis 的應用都是以乙個 sqlsessionfactory 的例項為中心的。sqlsessionfactory 的例項可以通過 sqlsessionfactorybuilder 獲得。而 sqlsessionfactorybuilder 則可以從 xml mybatis conf...