幾乎所有的Node.js開發人員可以告訴你`require()`函數做什么,但我們又有多少人真正知道它是如何工作的?我們每天都使用它來加載庫和模塊,但它的行為,對于我們來說反而是一個謎。
出于好奇,我鉆研了node的核心代碼來找出在引擎下發生了什么事。但這并不是一個單一的功能,我在node的模塊系統的找到了module.js。該文件包含一個令人驚訝的強大的且相對陌生的核心模塊,控制每個文件的加載,編譯和緩存。`require()`,它的橫空出世,只是冰山的一角。
module.js
該模塊的第二大任務是處理node的模塊加載機制。我們使用的獨立操作的“require”函數實際上是一個抽象概念的module.require,這本身就是只是一個簡單的關于Module._load功能的封裝。此load方法處理每個文件的實際加載,并在那里開始我們的旅程。
Module._load
Module._load負責加載新的模塊和管理模塊的緩存。緩存加載的每個模塊減少冗余文件的讀取次數,并可以顯著地加快您應用程序的速度。此外,共享模塊實例允許單例特性的模塊,保持在項目中的狀態。
如果某個模塊沒有在緩存中存在,Module._load將創建該文件的一個新的基本模塊。然后,它會告訴模塊在將它們發送到module._compile之前閱讀新文件的內容。[1]
如果您注意到上面的步驟#6,你會看到module.exports已被返回給用戶。這就是為什么當你在定義公共接口使用時,你使用exports和module.exports,因為Module._load將接下來返回require的內容。我很驚訝,這里沒有更多的功能,但如果有的話那更好。
module._compile
? 這是真正的奇跡發生的地方。首先,一個特殊的獨立操作的require函數是為該模塊創建的。這是我們需要的并且都熟悉的功能。而函數本身只是一個在Module.require的封裝,它也包含了一些便于我們使用的鮮為人知的輔助方法:
? require():加載一個外部模塊
? require.resolve():解析一個模塊名到它的絕對路徑
? require.main:主模塊
? require.cache:所有緩存好的模塊
? ?require.extensions:根據其擴展名,對于每個有效的文件類型可使用的編制方法
一旦require準備好了,整個加載的源代碼就會被封裝在一個新的函數里,可以接受require,module,exports和所有其他暴露的變量作為參數。這是一個僅僅為封裝模塊的而創建的函數,以便于在防止與Node.js的環境產生沖突。
結論
因此,我們已經了解了require的全部代碼,并已經初步了解它是如何工作的。
如果你已經按照這一切的方式做了,那么你已經為最后的秘密做好準備:require('module')。這是正確的,該模塊系統本身可以通過模塊系統被加載。盜夢空間。這可能聽起來很奇怪,但它可以讓用戶空間同模塊加載系統互動起來,并不需要鉆研Node.js核心。受歡迎的模塊都像這樣被建立。[2]
如果您想了解更多,請自己查看module.js源代碼。還有很多東西足夠你頭痛一段時間了。第一個可以告訴我什么是NODE_MODULE_CONTEXTS“以及它為什么被添加的人可以得到加分獎勵 :)
[1] module._compile方法只用于運行JavaScript文件。 JSON文件需通過JSON.parse() 解析并返回
[2]然而,這兩個模塊都建立在私有模塊的方法,如Module._resolveLookupPaths和Module._findPath。你可以認為這并沒有好多了...
新聞熱點
疑難解答