在ES2015規範以前,JavaScript這門語言自己沒有提供組織代碼的方式, Node.js用CommonJS模塊規範填補了這個空白,在這篇文章裏面, 咱們將會討論node.js的模塊機制以及如何在項目中組織模塊javascript
模塊是代碼結構的基本構建單元,模塊容許你組織你本身的代碼,隱藏私有數據,經過module.exports
暴露公共接口,每當你調用require
的時候,你就在加載一個模塊vue
這是一個很簡單的使用CommonJS的例子java
// multiply.js
function multiply(a, b) {
return a * b
}
module.exports = multiply
複製代碼
這個multiply.js文件就是一個模塊,要想使用它,咱們只須要require它便可node
const multiply = require('multiply')
console.log(multiply(2, 3)) // 6
複製代碼
在背後,multiply.js被node.js用下面的方式包裝了npm
(function (exports, require, module, __filename, __dirname) {
function multiply(a, b) {
return a * b
}
module.exports = add
})
複製代碼
這就是爲何你能夠在你的代碼中獲取到像require和module這些全局變量,模塊機制也保證了變量的做用域限制在本地而不會暴露到全局緩存
在Node中引入模塊,須要經歷以下3個步驟。
路徑分析
文件定位
編譯執行函數
在Node中,模塊分爲兩類:一類是Node提供的模塊,稱爲核心模塊;另外一類是用戶編寫的模塊,稱爲文件模塊。post
核心模塊在Node源代碼的編譯過程當中,編譯進了二進制執行文件。在Node進程啓動時,部分核心模塊就被直接加載進內存中,因此這部分核心模塊引入時,文件定位和編譯執行這兩個步驟能夠省略掉,而且在路徑分析中優先判斷,因此它的加載速度是最 快的。
文件模塊則是在運行時動態加載,須要完整的路徑分析、文件定位、編譯執行過程,速度比核心模塊慢。ui
node的模塊加載機制是在第一次調用requrie以後緩存模塊,以後這個模塊的調用都是從緩存中讀取,也就是每當你使用`require('a-module'), 你將會獲得a-module的同一個實例,這保證了模塊是單例的,同一個模塊無論require多少次,在應用中都保持一個狀態this
你能夠加載原生模塊,路徑引用來自你的文件系統或者已經安裝的模塊,若是傳給require函數的標識符不是一個原生的模塊也不是一個文件的引用('./../'),node.js將會查找已經安裝的模塊,他會遍歷你的文件目錄裏面的node_modules文件夾,來查找引用的模塊
處理模塊加載的是node的核心模塊是module.js
,能夠在node倉庫的lib/module.js找到它,其中最重要的是函數_load
和_complie
這個函數檢查當前的模塊是否是存在緩存中,若是在緩存中,則返回導出的對象
若是模塊是原生的,會傳入filename作參數,調用NativeModule.require()方法,返回結果
不然,這個函數爲文件建立一個新模塊而且將其保存在緩存中,返回導出對象,流程以下
編譯函數在隔離的做用域或者沙箱裏面運行文件,對這個文件暴露了require,module, exports這些幫助變量
在應用中,咱們在建立模塊的時候須要處理好內聚和耦合的平衡,最理想的場景就是實現高內聚和低耦合
一個模塊要想實現高內聚就須要專一一個功能,低耦合意味着模塊不該該有全局或共享的狀態,他們應該僅經過傳遞參數來通信,這樣即便模塊被替換也不須要改動太多的代碼庫
建議像下面這樣單獨暴露命名的函數和常量
const COUNT_NUMBER = 0
function count () { /* ... */ }
module.exports = {
COUNT_NUMBER,
count
}
複製代碼
而不是暴露一個含有各類屬性的對象
const COUNT_NUMBER = 0
function count () { /* ... */ }
let obj = {COUNT_NUMBER, count}
module.exports = obj
複製代碼
有兩種主要的方式寫Node模塊
一種是硬編碼依賴,經過調用require,顯式地加載一個模塊到另一個模塊,這種是最多見的使用方法,這種方式咱們用node來管理模塊的生命週期,直接易懂,並且也方便調試
第二種是依賴注入的方式
這種方式在node的環境中不多使用,但它是一個頗有用的概念,依賴注入模式能夠下降模塊間的解耦,這種模式不是顯式爲模塊定義依賴,而是從外面接收,所以很容易用一個有一樣接口的模塊來替代
咱們用工廠模式建立一個依賴注入的模塊,來講明下這種方式
class Car {
constructor (options) {
this.engine = options.engine
}
start () {
this.engine.start()
}
}
function create (options) {
return new Car(options)
}
module.exports = create
複製代碼
當咱們使用create這個方法的時候,只要傳遞的options有engine屬性,而且engine屬性實現了start方法就能夠了,換句話說,無論你是啥車,自行車也能夠,只要有引擎,而且引擎有啓動的方法,我就能夠生產一臺車,哪怕有一天你的車升級了,能夠在水上開,空中飛也不要緊,這個create模塊我依然不須要修改