您好,登錄后才能下訂單哦!
這篇文章主要講解了“Nodejs模塊機制介紹”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Nodejs模塊機制介紹”吧!
Node
應用由模塊組成,其模塊系統借鑒了CommonJS
模塊規范,但是并未完全按照規范實現,而是根據自身需求增加了一些特性,算是CommonJS
模塊規范的一個變種。
CommonJS
是社區提出的一種JavaScript
模塊化規范,可以說是JS
模塊化歷程中最重要的一塊里程碑,它構造了一個美好的愿景——JS
能夠在任何地方運行,但其實由于它的模塊是同步加載的,只適合在服務端等其他本地環境,并不適合瀏覽器端等需要異步加載資源的地方。
為了能讓JS
能夠在任何地方運行,CommonJS
制定了一些接口規范,這些接口覆蓋了模塊、二進制、Buffer
、字符集編碼、I/O
流、進程環境、文件系統、socket
、單元測試、web服務器、網關、包管理等等,雖然大部分都處于草案階段,但是其深深影響了Node
的發展。
下圖表示了Node
與瀏覽器、W3C
、CommonJS
以及ECMAScript
之間的關系,摘自 《深入淺出NodeJS》
CommonJS
的模塊主要由模塊引用、模塊定義和模塊標識三部分組成。
模塊標識
模塊標識對于每個模塊來說是唯一的,是它被引用時的依據,它必須是符合小駝峰命名的字符串,或者是文件的相對路徑或絕對路徑。
require('fs')// fs是內建模塊,執行時會被直接載入內存,無須路徑標識 require('./moduleA')//導入當前目錄的moduleA require('../moduleB')// 導入上一個目錄的moduleB require('C://moduleC')// 絕對路徑導入moduleC
模塊引用
使用require()
來引用一個模塊,這個方法接受一個模塊標識作為參數,以此引入一個模塊的API到當前上下文中。
const fs = require('fs')// 引入內建的fs模塊
模塊定義
有導入自然也有導出,要將當前上下文中的方法或變量作為模塊導出,需要使用內建的module.exports
對象,它是模塊導出的唯一出口。
CommonJS
規范規定,每個模塊內部,module
變量代表當前模塊。這個變量是一個對象,它的exports
屬性(即module.exports
)是對外的接口。加載某個模塊,其實是加載該模塊的module.exports
屬性。
// moduleA.js模塊 let moduleA = { name:"moduleA" } module.exports = { moduleA } // moduleB.js模塊 // 導入moduleA const {moduleA} = require('./moduleA')
CommonJS
模塊的特點如下:
每個模塊具有獨立的上下文,模塊內的代碼獨立執行,不會污染全局作用域。
模塊可以被多次加載,但是只會在第一次加載時運行,運行結果會被緩存,后續再加載相同模塊會直接讀取緩存結果,緩存存儲在module.cache
中
模塊的加載按代碼順序執行。
Node
導入模塊需要經歷3
個步驟:路徑分析 -> 文件定位 -> 編譯執行:
路徑分析:根據模塊標識分析模塊類型。
文件定位:根據模塊類型和模塊標識符找到模塊所處位置。
編譯執行:將文件編譯成機器碼執行,中間需要經過一系列轉化。
模塊類型分為內建模塊和用戶模塊:
內建模塊:內建模塊由Node
提供,已經被編譯成二進制執行文件,在node
執行時,內建模塊會被直接載入內存,因此我們可以直接引入,它的加載速度很快,因為它不需要經過文件定位和編譯執行這2
個步驟。
文件模塊:使用js
或C++
等編寫的擴展模塊,執行時需要先被編譯成二進制機器碼。需要經過上述三大步驟。
模塊緩存
不管是內建模塊還是文件模塊,node
在第一次加載后都會將結果緩存起來,下次加載相同模塊時,會先從緩存中查找,如果能查找到則直接從緩存中讀取,緩存的結果是模塊編譯和執行后的對象,是所有模塊中加載最快的。
路徑分析
路徑分析依據的是模塊標識符,模塊標識符有以下幾種類型:
內建模塊標識,例如fs
,path
等,不需要編譯,node
運行時被直接載入內存等待導入。
相對路徑模塊標識:使用相對路徑描述的文件模塊
絕對路徑模塊標識:使用絕對路徑描述的文件模塊
自定義模塊標識:通常是node_modules
中的包,引入時也不需要寫路徑描述,node
有一套算法來尋找,是所有模塊標識中分析速度最慢的。
文件定位
文件定位主要包括文件擴展名分析、目錄和包的處理。如果文件定位結束時都沒找到任何文件,則會拋出文件查找失敗的異常。
文件擴展名分析
由于模塊標識可以不添加文件擴展名,因此Node
會按.js
、.json
、.node
的次序依次補足擴展名來嘗試加載,嘗試加載的過程需要調用fs
模塊同步阻塞式地判斷文件是否存在,因此為了提高性能,可以在使用require()
導入模塊時,參數帶上文件擴展名,這樣會加快文件定位速度。
目錄、包的處理
在分析文件擴展名時,可能得到的是一個目錄,此時Node
會將其作為一個包處理,用查找包的規則來查找:在當前目錄下查找package.json
,獲得其中定義的main
屬性指定的文件名,以它來作為查找的入口,如果沒有package.json
,則默認將目錄下的index
當前默認文件名,然后依次查找index.js
、index.json
、index.node
。
編譯執行
編譯和執行是模塊導入的最后一個步驟,node
會先創建一個Module
實例,代表當前模塊。它有以下屬性:
module.id
模塊的識別符,通常是帶有絕對路徑的模塊文件名。
module.filename
模塊的文件名,帶有絕對路徑。
module.loaded
返回一個布爾值,表示模塊是否已經完成加載。
module.parent
返回一個對象,表示調用該模塊的模塊。
module.children
返回一個數組,表示該模塊要用到的其他模塊。
module.exports
表示模塊對外輸出的值。
通過文件定位得到的信息,Node
再載入文件并編譯。對于不同的文件擴展名,其載入方法也有所不同:
.js
文件:通過fs
模塊同步讀取文件后編譯執行。
.node
文件:這是C/C++
編寫的擴展文件,通過dlopen()
方法加載。
.json
文件:通過fs
模塊讀取后,用JSON.parse()
解析返回結果。
其余擴展名一律當.js
文件載入
每一個載入的模塊都會被緩存,可以通過require.cache
來查看。
目前,在node
中使用ES-Module
屬于實驗性功能,從8.5
開始支持,執行時需要加上--experimental-modules
參數。從12.17.0 LTS
開始,去掉了--experimental-modules
,現在可以通過使用.mjs
文件代替.js
文件或在package.json
中指定 type
為 module
兩種方式使用。
// package.json { "name": "esm-project", "version": "1.0.0", "main": "index.js", "type": "module", ... }
ES-Module
相比于CommonJS
的Module
機制,最大不同是ES-Module
對導出模塊的變量、對象是動態引用,而且是在編譯階段暴露模塊的導入接口,因此可以進行靜態分析;而CommonJS-Module
是運行時同步加載,且輸出的是導出模塊的淺拷貝。除此之外,ES-Module
支持加載CommonJS-Module
,而反過來則不行。
其次,Node
規定 ES6
模塊之中不能使用 CommonJS
模塊的特有的一些內部變量,這是因為ES-Module
頂層this
指向undefined
,CommonJS
模塊的頂層this
指向當前模塊,而這些內部變量作為頂層變量能被直接使用。
CommonJS
的內部變量有:
arguments
require
module
exports
m
__filename
__dirname
Node
模塊的加載是同步的,只有加載完成,才能執行后面的操作。
每一個文件就是一個模塊,有自己的作用域。每個模塊內部,module
對象代表了當前模塊,它的exports
屬性作為當前模塊的導出接口。
導入的模塊是導出模塊的一個淺拷貝。
感謝各位的閱讀,以上就是“Nodejs模塊機制介紹”的內容了,經過本文的學習后,相信大家對Nodejs模塊機制介紹這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。