Node.js中require的工作原理淺析
幾乎所有的Node.js開發(fā)人員可以告訴你`require()`函數(shù)做什么,但我們又有多少人真正知道它是如何工作的?我們每天都使用它來加載庫和模塊,但它的行為,對于我們來說反而是一個謎。
出于好奇,我鉆研了node的核心代碼來找出在引擎下發(fā)生了什么事。但這并不是一個單一的功能,我在node的模塊系統(tǒng)的找到了module.js。該文件包含一個令人驚訝的強(qiáng)大的且相對陌生的核心模塊,控制每個文件的加載,編譯和緩存。`require()`,它的橫空出世,只是冰山的一角。
module.js
function Module(id, parent) {
this.id = id;
this.exports = {};
this.parent = parent;
// ...
在module.js在Node.js內(nèi)部主要承擔(dān)兩個角色。首先,它為所有的Node.js模塊提供了一個基礎(chǔ)。每個文件是基本模塊new出的一個新實例,即使在該文件已經(jīng)運(yùn)行之后,仍然存在。這就是為什么我們能夠性為module.exports附加屬并在需要時返回它們。
該模塊的第二大任務(wù)是處理node的模塊加載機(jī)制。我們使用的獨立操作的“require”函數(shù)實際上是一個抽象概念的module.require,這本身就是只是一個簡單的關(guān)于Module._load功能的封裝。此load方法處理每個文件的實際加載,并在那里開始我們的旅程。
Module._load
Module._load = function(request, parent, isMain) {
// 1. Check Module._cache for the cached module.
// 2. Create a new Module instance if cache is empty.
// 3. Save it to the cache.
// 4. Call module.load() with your the given filename.
// This will call module.compile() after reading the file contents.
// 5. If there was an error loading/parsing the file,
// delete the bad module from the cache
// 6. return module.exports
};
Module._load負(fù)責(zé)加載新的模塊和管理模塊的緩存。緩存加載的每個模塊減少冗余文件的讀取次數(shù),并可以顯著地加快您應(yīng)用程序的速度。此外,共享模塊實例允許單例特性的模塊,保持在項目中的狀態(tài)。
如果某個模塊沒有在緩存中存在,Module._load將創(chuàng)建該文件的一個新的基本模塊。然后,它會告訴模塊在將它們發(fā)送到module._compile之前閱讀新文件的內(nèi)容。[1]
如果您注意到上面的步驟#6,你會看到module.exports已被返回給用戶。這就是為什么當(dāng)你在定義公共接口使用時,你使用exports和module.exports,因為Module._load將接下來返回require的內(nèi)容。我很驚訝,這里沒有更多的功能,但如果有的話那更好。
module._compile
Module.prototype._compile = function(content, filename) {
// 1. Create the standalone require function that calls module.require.
// 2. Attach other helper methods to require.
// 3. Wraps the JS code in a function that provides our require,
// module, etc. variables locally to the module scope.
// 4. Run that function
};
· 這是真正的奇跡發(fā)生的地方。首先,一個特殊的獨立操作的require函數(shù)是為該模塊創(chuàng)建的。這是我們需要的并且都熟悉的功能。而函數(shù)本身只是一個在Module.require的封裝,它也包含了一些便于我們使用的鮮為人知的輔助方法:
· require():加載一個外部模塊
· require.resolve():解析一個模塊名到它的絕對路徑
· require.main:主模塊
· require.cache:所有緩存好的模塊
· ·require.extensions:根據(jù)其擴(kuò)展名,對于每個有效的文件類型可使用的編制方法
一旦require準(zhǔn)備好了,整個加載的源代碼就會被封裝在一個新的函數(shù)里,可以接受require,module,exports和所有其他暴露的變量作為參數(shù)。這是一個僅僅為封裝模塊的而創(chuàng)建的函數(shù),以便于在防止與Node.js的環(huán)境產(chǎn)生沖突。
(function (exports, require, module, __filename, __dirname) {
// YOUR CODE INJECTED HERE!
});
該Module._compile方法是同步執(zhí)行的,所以對Module._load的調(diào)用只能等到這段代碼運(yùn)行結(jié)束,并將module.exprts返回給用戶。
結(jié)論
因此,我們已經(jīng)了解了require的全部代碼,并已經(jīng)初步了解它是如何工作的。
如果你已經(jīng)按照這一切的方式做了,那么你已經(jīng)為最后的秘密做好準(zhǔn)備:require('module')。這是正確的,該模塊系統(tǒng)本身可以通過模塊系統(tǒng)被加載。盜夢空間。這可能聽起來很奇怪,但它可以讓用戶空間同模塊加載系統(tǒng)互動起來,并不需要鉆研Node.js核心。受歡迎的模塊都像這樣被建立。[2]
如果您想了解更多,請自己查看module.js源代碼。還有很多東西足夠你頭痛一段時間了。第一個可以告訴我什么是NODE_MODULE_CONTEXTS“以及它為什么被添加的人可以得到加分獎勵 :)
[1] module._compile方法只用于運(yùn)行JavaScript文件。 JSON文件需通過JSON.parse() 解析并返回
[2]然而,這兩個模塊都建立在私有模塊的方法,如Module._resolveLookupPaths和Module._findPath。你可以認(rèn)為這并沒有好多了...
相關(guān)文章
Node.js開發(fā)之套接字(socket)編程入門示例
這篇文章主要介紹了Node.js開發(fā)之套接字(socket)編程,結(jié)合簡單實例形式分析了node.js套接字socket客戶端與服務(wù)器端相關(guān)實現(xiàn)與使用技巧,需要的朋友可以參考下2019-11-11詳解Node.js?應(yīng)用高?CPU?占用率分析方法
這篇文章主要為大家介紹了Node.js?應(yīng)用高?CPU?占用率分析方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10nodejs中使用HTTP分塊響應(yīng)和定時器示例代碼
本文通過示例將要創(chuàng)建一個輸出純文本的HTTP服務(wù)器,輸出的純文本每隔一秒會新增100個用換行符分隔的時間戳。實例代碼非常不錯,具有參考借鑒價值,需要的朋友參考下2017-03-03node-red教程之dashboard簡介與輸入型儀表板控件的使用
Node-red支持自定義節(jié)點,當(dāng)然也就支持自定義圖形化的節(jié)點。也有優(yōu)秀的開發(fā)者把自己建立的圖形化節(jié)點無償分享。這里給出一個股票界面的例子,讓大家看一看優(yōu)秀的node-red界面能做到什么樣子2022-01-01Windows系統(tǒng)下Node.js的簡單入門教程
這篇文章主要介紹了Windows系統(tǒng)下Node.js的簡單入門教程,Node.js是用于后端編程的JavaScript框架,需要的朋友可以參考下2015-06-06node.js的exports、module.exports與ES6的export、export default深入詳解
這篇文章主要給大家介紹了關(guān)于node.js中的exports、module.exports與ES6中的export、export default到時是什么的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-10-10