做爲還在漫漫前端學習路上的一位自學者。我以學習分享的方式來整理本身對於知識的理解,同時也但願可以給你們做爲一份參考。但願可以和你們共同進步,若有任何紕漏的話,但願你們多多指正。感謝萬分!html
以編程角度來講, "模塊" 指的是可以提供必定功能或數據的程序語句集合. 模塊具有和外部聯繫的接口 (其餘模塊或程序調用該模塊的方式)前端
在 Node.js 中, 每一個文件就被視爲一個模塊. 這個文件多是 JavaScript 編寫的文件、JSON 或者用 C/C++ 編譯的二進制文件. 經過對外接口來向外部暴露功能或者數據, 模塊之間能夠互相調用.node
隨着開發複雜度的提高, 將代碼都寫在一處的傳統開發方式, 顯現出了不少問題:npm
經過使用模塊機制, 咱們能夠把一個複雜程序的各個功能拆分, 分別封裝到不一樣的模塊. 每一個模塊職責單一 (各管一件事, 之間沒交集) 經過開發新模塊, 和對已有模塊的複用來實現各類功能. 這種開發方式被稱爲 "模塊化開發".編程
應用模塊化開發, 使得各個功能都封裝在獨立的文件中, 分而治之, 互不干擾. 使得代碼易於維護和複用. 同時每一個模塊中的變量也不會污染全局做用域, 避免了命名衝突.json
Node.js 參照 CommonJS 標準實現了模塊機制. CommonJS 是一套代碼規範, 目的是爲了構建 JavaScript 在瀏覽器以外的生態系統 (服務器端, 桌面端). JavaScript 誕生之初只是爲了寫網頁小腳本, 並不做爲開發大型複雜應用的語言, 其自身有不少不足. 而且, 官方規範 (ECMAScript) 制定的時間較早, 涵蓋範圍較小, 對於後端開發而言, 例如文件系統, I/O 流, 模塊系統, 等等方面都沒有相應的標準. 基於種種的不足, CommonJS 規範致力於彌補 JavaScript 沒有標準的缺陷, 讓 JavaScript 有能力去開發複雜應用, 同時具有跨平臺能力.後端
下面是一個 Node.js 的模塊使用示例:瀏覽器
在代碼中, 開頭經過 require
方法引入了 Node.js 自帶的 http
模塊. 並用此模塊實現了一個 HTTP 服務器.服務器
const http = require('http');
function myNodeServer(req, res){
res.writeHead(200, {'Content-type':'text/plain'});
res.write('Hello World');
res.end();
}
http.createServer(myNodeServer).listen(3000); //監聽 3000 端口
console.log('Server is running!');
複製代碼
前文說, 在 Node.js 中, 每一個文件就被視爲一個模塊. 這個文件多是 JavaScript 編寫的文件、JSON 或者用 C/C++ 編譯的二進制文件.網絡
模塊能夠分紅三類:
http
, fs
, url
. 其分爲 C/C++ 編寫的和 JavaScript 編寫的兩部分. C/C++ 模塊存放在 Node.js 源代碼目錄的 src/
目錄下. JavaScript 模塊存放在 lib/
目錄下. NPM 是隨同 Node.js 一塊兒安裝的 "包管理工具". 經過它, 全世界開發者們能夠簡單方便地互相分享和借鑑各自的 Node.js 模塊. 其讓整個 Node.js 社區生態變得繁榮熱鬧.
NPM 常見的使用場景有如下幾種:
具體的使用方法網上有不少教程, 這裏就不贅述了. 不想自行查閱的話, 能夠直接參考下面的連接:
在瞭解了什麼是模塊以後, 讓咱們來看看如何在 Node.js 中實際應用模塊機制. 在使用上, 能夠很簡單的分爲三個步驟: 建立, 導出, 引入. 先建立一個模塊, 而後導出功能或數據, 模塊之間能夠互相引入導出的內容.
Node.js 提供了 exports
和 require
兩個對象,其中 exports
用於導出模塊, require
用於從外部引入另外一個模塊, 即獲取模塊的 exports
對象.
先讓咱們來看看如何建立並把模塊的內容導出. 在 Node.js 中, 一個文件就是一個模塊. 建立模塊的方法就是建立一個文件.
經過 exports
對象來指定一個模塊的導出內容.
示例:
// 文件名: nameModule.js
var name = 'Garrik';
exports.setName = function(newName) {
name = newName;
}
exports.getName = function() {
return name;
}
複製代碼
在以上示例中, nameModule.js 文件經過 exports
對象將 setName
和 getName
做爲模塊的訪問接口. 其餘的模塊能夠引入導出的 exports
對象, 直接訪問 exports
對象的成員函數.
在 Node.js 中, 經過 require
函數來引入外界模塊導出的內容. require
函數接受一個字符串做爲路徑參數, 函數根據這個字符串參數來進行模塊查找. 找到後會返回目標模塊導出的 exports
對象.
示例:
// 文件名: showNameModule.js
var nameModule = require('./nameModule.js');
console.log(nameModule.getName());
// 顯示: Garrik
nameModule.setName('Xiang');
console.log(nameModule.getName());
// 顯示: Xiang
複製代碼
上面示例中, 經過 require
引入了當前目錄下 nameModule.js 導出的 exports
對象, 並讓一個本地變量指向引入模塊的 exports
對象. 以後在 showNameModule.js 文件中就可使用 getName
和 setName
這兩個方法了.
在使用 exports
對象導出內容時, 全部做爲對外訪問接口的屬性和方法都是定義在 exports
屬性上的. 上面的例子中 setName
和 getName
方法都直接定義在 exports
對象上. 那若是想直接導出一個對象, 或者基礎類型值可不能夠呢?
可能有人會想可不能夠這樣寫:
var name = 'Garrik';
exports = name;
複製代碼
若是你試一下的話會發現, 最後引入的是一個空對象, 而不是你定義在 exports
上的東西.
在使用 exports
的時候只能往這個對象裏添加新的屬性和方法, 而不能對其直接賦值. 若是想直接導出一個對象, 或者基礎類型值要使用 module.exports
對象. 例如上面例子就能夠改寫成:
// 文件名: nameModule.js
var name = 'Garrik';
module.exports = {
setName: function(newName) {
name = newName;
},
getName: function() {
return name;
}
}
複製代碼
這樣寫的話, 就導出了一整個對象, setName
和 getName
方法是這個對象的成員函數. 而不是以前的 exports
對象了.
除此以外 module.exports
還能夠直接導出基礎類型值:
// 文件名: numMoule.js
var num = 123456;
module.exports = num;
複製代碼
// 文件名: showNum.js
var getNum = require('./numModule.js'); // showNum.js 和 numModule.js 在同一目錄下
console.log(getNum); // 結果: 123456
複製代碼
這種方式下, 導出的就直接是基礎類型的值.
可能仍是不少人在疑惑 exports
和 module.exports
區別和關係.
上面我說, 一個文件被另外一個模塊引入時, 會被作一些處理. 文件中代碼並不被 Node 執行, 而是被打包進一個函數中, 而後 Node 執行這個函數. 打包函數會被傳入 exports
,require
,module
,__filename
,__dirname
這五個參數. 全部的這些參數都在 Node.js 執行函數時賦值, 而且只在當前的函數做用域中有效. 打包函數執行到最後, 返回 module.exports
對象.
其中, exports
是 module.exports
的引用, module
對象表明被打包進去的代碼自己. module
的 exports
對象用於指定一個模塊的導出內容.
在模塊中定義外部可訪問接口的時候, 有兩個方法:
exports.name = 'Garrik';
複製代碼
module.exports = {name: 'Garrik'};
複製代碼
在使用 exports
的時候只能往這個對象裏添加新的屬性和方法, 而不能對其直接賦值. 由於直接賦值會打破其對 module.exports
的引用.
// 這是能夠的:
exports.name = 'Garrik';
exports.gender = 'Male';
// 這是不能夠的:
exports = {name: 'Garrik', gender: 'Male'};
// 應該用 module.exports:
module.exports = {name: 'Garrik', gender: 'Male'}
複製代碼
若是想直接導出一個對象, 或基本類型值, 應該使用 module.exports
.
// 導出函數
module.exports = function(num) {
return num + 1;
};
// 導出基本類型值
module.exports = 123;
複製代碼
在用 require
引入模塊時, 路徑參數可能有下面三種形式:
./
開頭 或 ../
開頭/
開頭http
, fs
, url
)根據參數不一樣, 加載方式也有區別.
在指定了模塊路徑的狀況下, Node.js 會去指定的位置加載模塊. 但由於用 require
來加載模塊時能夠省略文件後綴, 在省略的狀況下, Node.js 會去猜想文件的類型.
比方說我要去 ./modules/
目錄下加載一個 haha
模塊.
var haha = require('./modules/haha');
複製代碼
由於 haha
沒寫文件後綴, Node.js 將執行的操做順序爲:
haha
爲一個目錄, 則先查找該文件夾下的 package.json 文件,而後再加載該文件當中 main
字段所指定的入口文件. 若 package.json 文件當中沒有 main
字段,或者根本沒有 package.json 文件,則再默認查找該文件夾下的 index.js 文件, 並做爲模塊來載入.在沒有路徑, 參數值直接爲一個模塊名的狀況下:
var haha = require('haha');
複製代碼
haha
是 Node.js 核心模塊就直接加載.haha
的所在. 如有兩個同名文件,則遵循就近原則。優先引入目錄順序靠前的模塊.haha
爲一個目錄, 則先查找該文件夾下的 package.json 文件,而後再加載該文件當中 main
字段所指定的入口文件. 若 package.json 文件當中沒有 main
字段,或者根本沒有 package.json 文件,則再默認查找該文件夾下的 index.js 文件, 並做爲模塊來載入.😆 好啦,今天的分享就告一段落啦。下一篇中,我會介紹如何實現一個 "Hello World" HTTP 服務器。
傳送門: Node.js 系列 - 搭建 "Hello World" HTTP 服務器
若是喜歡的話就點個關注吧!O(∩_∩)O 謝謝各位的支持❗️