模塊引用
示例代碼如下:
const fs = require("fs");
在 CommonJS 規(guī)范中桦踊,require 接受一個模塊標(biāo)志亚享,以此引入模塊的 API劣欢。
模塊提供了 exports 對象來導(dǎo)出方法或變量,另外還有一個 module 對象纪蜒,該對象即模塊本身,而在 nodejs 中此叠,文件就是模塊纯续。在 module 對象上有一個 module.exports 屬性,這是其導(dǎo)出的內(nèi)容灭袁,變量 exports 指向的地址就是 module.exports猬错。也就是說:
module.exports === exports
這里注意,可以在 exports 上添加屬性或方法來導(dǎo)出茸歧,但不可修改 exports 本身的值倦炒,因為改了以后,exports 不在指向 module.exports, 也就不會被導(dǎo)出举娩。如果想要導(dǎo)出一個類析校,可以:
let A = {}
A.prototype.foo = foo;
...
module.exports = A;
node 模塊實現(xiàn)
node 會將加載過的模塊放入緩存构罗,下次引用直接從緩存加載。
路徑分析 和 文件定位
- 核心模塊智玻,如 fs 遂唧、path、http 等吊奢,直接引用模塊名盖彭。node 啟動時就已經(jīng)加載到內(nèi)存,加載速度最快
- "." 或".."開頭页滚,相對路徑查找召边,知道路徑,查找快裹驰,但仍需動態(tài)加載隧熙,速度稍慢
- "/"開頭,從根目錄查找幻林,同上
- 自定義模塊贞盯,根據(jù)
module.paths
變量遞歸向上查找 node_modules 目錄
文件定位
require 查找模塊時,需要 fs 模塊同步阻塞的判斷是否存在沪饺。
require 時一般不需要指定文件后綴名躏敢,但也可以加上。如果沒有后綴整葡,node 會依次在對應(yīng)路徑查找 .js
件余、.json
、.node
遭居。如果是后兩種啼器,加上后綴名查找會稍快。
很可能最后找不到對應(yīng)的.js
魏滚、.json
镀首、.node
文件,但找到的是一個目錄鼠次。則會查看該目錄package.json
下main 項對應(yīng)的值更哄。示例如下:
"version": "1.0.0",
"description": "",
"main": "webpack.config.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
于是找到了 webpack.config.js 文件。如果沒有 main 項或者不存在 package.json腥寇,則會依次查找 index.js , index.json, index.node成翩。如果仍然沒有,就按照 module.path 數(shù)組依次遞歸向上查找赦役。最終找不到麻敌,則拋出異常。
模塊編譯
node 中模塊定義如下:
function Module(id, parent) {
this.id = id;
this.parent = parent;
this.exports = {};
if(parent && parent.children){
parent.children.push(this);
}
this.filename = null; //定義時還不能確定該值
this.loaded = false;
this.children = [];
}
定位到具體文件后掂摔,對不同類型的文件操作不一樣:
- .js 文件术羔,通過 fs 模塊同步讀取后編譯執(zhí)行
- .node 文件赢赊,這是 c/c++ 寫的擴(kuò)展文件
- .json 文件,讀取后通過 JSON.parse() 解析并返回結(jié)果
每一個編譯后的模塊都被緩存起來级历。
javaScript 模塊的編譯
我們前面知道有 require 方法和 exports 對象释移,可是這些變量和方法在哪里聲明的呢?實際上寥殖,node 對讀取到的 js 文件做了包裝:
(function (exports, require, module, __filename, __dirname) {
// module content
});
node 讀取 js 文件后執(zhí)行的就是這個包裝函數(shù)玩讳,然后得到 module.exports 。
(待續(xù))