本篇文章給大家分享的是有關Nodejs中的模塊系統該如何使用,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
成都創新互聯是一家專業提供平武企業網站建設,專注與成都網站制作、網站設計、html5、小程序制作等業務。10年已為平武眾多企業、政府機構等服務。創新互聯專業網站建設公司優惠進行中。
早期 JavaScript 是為了實現簡單的頁面交互邏輯, 但隨著時代發展, 瀏覽器不單單僅只能呈現簡單交互, 各種各樣的網站開始大放光彩。隨著網站開始變得復雜化,前端代碼日漸增多,相對比起其他靜態語言,JavaScript 缺少模塊化的弊端開始暴露出來,如命名沖突。因此為了方便前端代碼的維護和管理,社區開始了對模塊化規范的定義。在這過程中,出現了很多的模塊化規范,如CommonJS, AMD, CMD, ES modules,本文章主要講解Node中根據CommonJS實現的模塊化。
首先,在 Node 世界里,模塊系統是遵守CommonJS規范的,CommonJS規范里定義,簡單講就是:
每一個文件就是一個模塊
通過module對象來代表一個模塊的信息
通過exports用來導出模塊對外暴露的信息
通過require來引用一個模塊
核心模塊: 如 fs,http,path 等模塊, 這些模塊不需要安裝, 在運行時已經加載在內存中。【推薦學習:《nodejs 教程》】
第三方模塊: 通過安裝存放在 node_modules 中。
自定義模塊: 主要是指 file 模塊,通過絕對路徑或者相對路徑進行引入。
我們上面說過,一個文件就是一個模塊,且通過一個 module 對象來描述當前模塊信息,一個 module 對象對應有以下屬性: - id: 當前模塊的id - path: 當前模塊對應的路徑 - exports: 當前模塊對外暴露的變量 - parent: 也是一個module對象,表示當前模塊的父模塊,即調用當前模塊的模塊 - filename: 當前模塊的文件名(絕對路徑), 可用于在模塊引入時將加載的模塊加入到全局模塊緩存中, 后續引入直接從緩存里進行取值 - loaded: 表示當前模塊是否加載完畢 - children: 是一個數組,存放著當前模塊調用的模塊 - paths: 是一個數組,記錄著從當前模塊開始查找node_modules目錄,遞歸向上查找到根目錄下的node_modules目錄下
說完CommonJS規范,我們先講下module.exports與exports的區別。
首先,我們用個新模塊進行一個簡單驗證
console.log(module.exports === exports); // true
可以發現,module.exports和epxorts實際上就是指向同一個引用變量。
demo1
// a模塊
module.exports.text = 'xxx';
exports.value = 2;
// b模塊代碼
let a = require('./a');
console.log(a); // {text: 'xxx', value: 2}從而也就驗證了上面demo1中,為什么通過module.exports和exports添加屬性,在模塊引入時兩者都存在, 因為兩者最終都是往同一個引用變量上面進行屬性的添加.根據該 demo, 可以得出結論: module.exports和exports指向同一個引用變量
demo2
// a模塊
module.exports = {
text: 'xxx'
}
exports.value = 2;
// b模塊代碼
let a = require('./a');
console.log(a); // {text: 'xxx'}上面的 demo 例子中,對module.exports進行了重新賦值, exports進行了屬性的新增, 但是在引入模塊后最終導出的是module.exports定義的值, 可以得出結論: noed 的模塊最終導出的是module.exports, 而exports僅僅是對 module.exports 的引用, 類似于以下代碼:
exports = module.exports = {};
(function (exports, module) {
// a模塊里面的代碼
module.exports = {
text: 'xxx'
}
exports.value = 2;
console.log(module.exports === exports); // false
})(exports, module)由于在函數執行中, exports 僅是對原module.exports對應變量的一個引用,當對module.exports進行賦值時,exports對應的變量和最新的module.exports并不是同一個變量
require引入模塊的過程主要分為以下幾步:
解析文件路徑成絕對路徑
查看當前需要加載的模塊是否已經有緩存, 如果有緩存, 則直接使用緩存的即可
查看是否是 node 自帶模塊, 如 http,fs 等, 是就直接返回
根據文件路徑創建一個模塊對象
將該模塊加入模塊緩存中
通過對應的文件解析方式對文件進行解析編譯執行(node 默認僅支持解析.js,.json, .node后綴的文件)
返回加載后的模塊 exports 對象

Module.prototype.require = function(id) {
// ...
try {
// 主要通過Module的靜態方法_load加載模塊
return Module._load(id, this, /* isMain */ false);
} finally {}
// ...
};
// ...
Module._load = function(request, parent, isMain) {
let relResolveCacheIdentifier;
// ...
// 解析文件路徑成絕對路徑
const filename = Module._resolveFilename(request, parent, isMain);
// 查看當前需要加載的模塊是否已經有緩存
const cachedModule = Module._cache[filename];
// 如果有緩存, 則直接使用緩存的即可
if (cachedModule !== undefined) {
// ...
return cachedModule.exports;
}
// 查看是否是node自帶模塊, 如http,fs等, 是就直接返回
const mod = loadNativeModule(filename, request);
if (mod && mod.canBeRequiredByUsers) return mod.exports;
// 根據文件路徑初始化一個模塊
const module = cachedModule || new Module(filename, parent);
// ...
// 將該模塊加入模塊緩存中
Module._cache[filename] = module;
if (parent !== undefined) {
relativeResolveCache[relResolveCacheIdentifier] = filename;
}
// ...
// 進行模塊的加載
module.load(filename);
return module.exports;
};至此, node 的模塊原理流程基本過完了。目前 node v13.2.0 版本起已經正式支持 ESM 特性。
在接觸 node 中,你是否會困惑 __filename, __dirname是從哪里來的, 為什么會有這些變量呢? 仔細閱讀該章節,你會對這些有系統性的了解。
順著上面的 require 源碼繼續走, 當一個模塊加載時, 會對模塊內容讀取
將內容包裹成函數體
將拼接的函數字符串編譯成函數
執行編譯后的函數, 傳入對應的參數
Module.prototype._compile = function(content, filename) {
// ...
const compiledWrapper = wrapSafe(filename, content, this);
//
result = compiledWrapper.call(thisValue, exports, require, module,
filename, dirname);
// ...
return result;
};function wrapSafe(filename, content, cjsModuleInstance) {
// ...
const wrapper = Module.wrap(content);
// ...
}
let wrap = function(script) {
return Module.wrapper[0] + script + Module.wrapper[1];
};
const wrapper = [
'(function (exports, require, module, __filename, __dirname) { ',
'\n});'
];
ObjectDefineProperty(Module, 'wrap', {
get() {
return wrap;
},
set(value) {
patched = true;
wrap = value;
}
});綜上, 也就是之所以模塊里面有__dirname,__filename, module, exports, require這些變量, 其實也就是 node 在執行過程傳入的, 看完是否解決了多年困惑的問題^_^
在package.json增加"type": "module"配置
// test.mjs
export default {
a: 'xxx'
}
// import.js
import a from './test.mjs';
console.log(a); // {a: 'xxx'}較明顯的區別是在于執行時機:
ES 模塊在執行時會將所有import導入的模塊會先進行預解析處理, 先于模塊內的其他模塊執行
// entry.js
console.log('execute entry');
let a = require('./a.js')
console.log(a);
// a.js
console.log('-----a--------');
module.exports = 'this is a';
// 最終輸出順序為:
// execute entry
// -----a--------
// this is a// entry.js
console.log('execute entry');
import b from './b.mjs';
console.log(b);
// b.mjs
console.log('-----b--------');
export default 'this is b';
// 最終輸出順序為:
// -----b--------
// execute entry
// this is bimport 只能在模塊的頂層,不能在代碼塊之中(比如在if代碼塊中),如果需要動態引入, 需要使用import()動態加載;
ES 模塊對比 CommonJS 模塊, 還有以下的區別:
沒有 require、exports 或 module.exports
在大多數情況下,可以使用 ES 模塊 import 加載 CommonJS 模塊。(CommonJS 模塊文件后綴為 cjs)
如果需要引入.js后綴的 CommonJS 模塊, 可以使用module.createRequire()在 ES 模塊中構造require函數
// test.cjs
export default {
a: 'xxx'
}
// import.js
import a from './test.cjs';
console.log(a); // {a: 'xxx'}// test.cjs
export default {
a: 'xxx'
}
// import.js
import a from './test.cjs';
console.log(a); // {a: 'xxx'}// test.cjs
export default {
a: 'xxx'
}
// import.mjs
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
// test.js 是 CommonJS 模塊。
const siblingModule = require('./test');
console.log(siblingModule); // {a: 'xxx'}沒有 __filename 或 __dirname
這些 CommonJS 變量在 ES 模塊中不可用。
沒有 JSON 模塊加載
JSON 導入仍處于實驗階段,僅通過 --experimental-json-modules 標志支持。
沒有 require.resolve
沒有 NODE_PATH
沒有 require.extensions
沒有 require.cache
在 CommonJS 中引入 ES 模塊
由于 ES Modules 的加載、解析和執行都是異步的,而 require() 的過程是同步的、所以不能通過 require() 來引用一個 ES6 模塊。
ES6 提議的 import() 函數將會返回一個 Promise,它在 ES Modules 加載后標記完成。借助于此,我們可以在 CommonJS 中使用異步的方式導入 ES Modules:
// b.mjs
export default 'esm b'
// entry.js
(async () => {
let { default: b } = await import('./b.mjs');
console.log(b); // esm b
})()在 ES 模塊中引入 CommonJS
在 ES6 模塊里可以很方便地使用 import 來引用一個 CommonJS 模塊,因為在 ES6 模塊里異步加載并非是必須的:
// a.cjs module.exports = 'commonjs a'; // entry.js import a from './a.cjs'; console.log(a); // commonjs a
至此,提供 2 個 demo 給大家測試下上述知識點是否已經掌握,如果沒有掌握可以回頭再進行閱讀。
demo module.exports&exports
// a模塊
exports.value = 2;
// b模塊代碼
let a = require('./a');
console.log(a); // {value: 2}demo module.exports&exports
// a模塊
exports = 2;
// b模塊代碼
let a = require('./a');
console.log(a); // {}require&_cache 模塊緩存機制
// origin.js
let count = 0;
exports.addCount = function () {
count++
}
exports.getCount = function () {
return count;
}
// b.js
let { getCount } = require('./origin');
exports.getCount = getCount;
// a.js
let { addCount, getCount: getValue } = require('./origin');
addCount();
console.log(getValue()); // 1
let { getCount } = require('./b');
console.log(getCount()); // 1根據上述例子, 模塊在 require 引入時會加入緩存對象require.cache中。 如果需要刪除緩存, 可以考慮將該緩存內容清除,則下次require模塊將會重新加載模塊。
let count = 0;
exports.addCount = function () {
count++
}
exports.getCount = function () {
return count;
}
// b.js
let { getCount } = require('./origin');
exports.getCount = getCount;
// a.js
let { addCount, getCount: getValue } = require('./origin');
addCount();
console.log(getValue()); // 1
delete require.cache[require.resolve('./origin')];
let { getCount } = require('./b');
console.log(getCount()); // 0以上就是Nodejs中的模塊系統該如何使用,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注創新互聯行業資訊頻道。
當前標題:Nodejs中的模塊系統該如何使用
文章URL:http://www.yijiale78.com/article18/jcsjgp.html
成都網站建設公司_創新互聯,為您提供外貿建站、網頁設計公司、關鍵詞優化、搜索引擎優化、用戶體驗、軟件開發
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯