天天看點

Node.js 子產品加載機制 Require()

require()

不要把這裡的require()和requirejs混為一談。不過有意思的是,typescript的子產品定義,甚至同時支援這兩種子產品機制。

導入和使用外部子產品,隻是簡單的一句require(),看看angular/material/docs下的編譯檔案gulpfile.js的代碼片段。對子產品導入和使用有個直覺的感覺。

gulp.task 用于定義了一個任務;cancat用于合并檔案;fs是一個對磁盤檔案操作的子產品。可以看出,有子產品的引入,代碼更為清晰而明确,這些常用子產品相當于對基本語言功能的擴充。

這裡,關鍵詞require()把一切聯系在一起。那麼這句簡單的語句背後發生了什麼事情呢?

require其實不是一個語言的關鍵詞,在文章後面的研究,我們就可以看到。

還沒有使用過require()或者對它實作機制不感興趣的開發人員,可以略個這一部分。确實,後面實作機制不太影響使用。

以下大部分内容都來自原文: how require() actually works

因為nodejs是開源的,我們可以追溯require()到node的核心代碼中去。但是,我們找到的不是一個簡單的函數,而是一個檔案module.js。這個檔案實作了node的整個子產品加載系統。涵蓋的過程有加載、編譯和緩存。而我們使用的require()隻是其冰上一角。

我們可以看到module.js首先定義了一個類型(函數)module。這個類型有兩個功能。一個,它是所有子產品的基類,之後每個子產品都是這個module的一個執行個體。這也是我們前面探讨的module.exports最終來源。 這個module的第二個功能就是完成node子產品的加載過程。我們使用的require()最終就是調用module.require方法,而這個方法又調用了另外一個内部方法module._load。最終的這個load方法才是真正加載子產品檔案的地方,也就是我麼将要分析研究的。

module._load負責裝載新子產品和管理子產品的緩存。緩存機制在每個子產品載入時減少重複讀取檔案,進而提高系統性能。另外,共享子產品執行個體還可以使得單例子產品在整個項目中保留狀态。 如果在緩存中沒有找到該子產品,module._load就會為該檔案建立一個新的module執行個體。并用該執行個體讀取檔案内容,然後發送給module._compile。 注意到在上面第6步,傳回了module.exports。這個傳回語句可以解釋,為什麼在你的子產品檔案中要把公開的接口(方法)賦給module.exports(或者别名exports);這也解釋了,require()傳回的變量,可以直接調用導入子產品的方法。到此,可以看到沒有任何神奇或特别的地方。

這個方法,一開始就建立require函數,這就是我們非常熟悉的那個require()(子產品裝載機制不僅僅有載入子產品的處理,也有導入和調用的流程,這裡可以看作調用方的流程,如我們提到的main.js)。而這個個函數本身隻是簡單的封裝了module.require和添加了一些幫助屬性和方法,如下:

require() 就是我們使用的require()

require.main 主子產品

require.cache 所有緩存的子產品

require.extensions 不同檔案類型(字尾)的編譯方法

在建構require之後,所有原檔案的代碼被封裝的一個新函數中,這個函數把require,module,exports作為參數。 這也可以解釋為什麼我們可以直接調用require(),為什麼exports是module.exports的别名。

(function(...){

})();

繼續閱讀