這篇文章給大家分享的是有關(guān)Node.js的模塊加載機(jī)制是什么的內(nèi)容。小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧。
站在用戶的角度思考問(wèn)題,與客戶深入溝通,找到海州網(wǎng)站設(shè)計(jì)與海州網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、域名與空間、網(wǎng)站空間、企業(yè)郵箱。業(yè)務(wù)覆蓋海州地區(qū)。模塊是Node.js里面一個(gè)很基本也很重要的概念,各種原生類庫(kù)是通過(guò)模塊提供的,第三方庫(kù)也是通過(guò)模塊進(jìn)行管理和引用的。本文會(huì)從基本的模塊原理出發(fā),到最后我們會(huì)利用這個(gè)原理,自己實(shí)現(xiàn)一個(gè)簡(jiǎn)單的模塊加載機(jī)制,即自己實(shí)現(xiàn)一個(gè)require
。
Node 使用 JavaScript 與 commonjs 模塊,并把 npm/yarn 作為其包管理器。
老規(guī)矩,講原理前我們先來(lái)一個(gè)簡(jiǎn)單的例子,從這個(gè)例子入手一步一步深入原理。Node.js里面如果要導(dǎo)出某個(gè)內(nèi)容,需要使用module.exports
,使用module.exports
幾乎可以導(dǎo)出任意類型的JS對(duì)象,包括字符串,函數(shù),對(duì)象,數(shù)組等等。我們先來(lái)建一個(gè)a.js
導(dǎo)出一個(gè)最簡(jiǎn)單的hello world
:
// a.js module.exports = "hello world";
然后再來(lái)一個(gè)b.js
導(dǎo)出一個(gè)函數(shù):
// b.js function add(a, b) { return a + b; } module.exports = add;
然后在index.js
里面使用他們,即require
他們,require
函數(shù)返回的結(jié)果就是對(duì)應(yīng)文件module.exports
的值:
// index.js const a = require('./a.js'); const add = require('./b.js'); console.log(a); // "hello world" console.log(add(1, 2)); // b導(dǎo)出的是一個(gè)加法函數(shù),可以直接使用,這行結(jié)果是3
當(dāng)我們require
某個(gè)模塊時(shí),并不是只拿他的module.exports
,而是會(huì)從頭開(kāi)始運(yùn)行這個(gè)文件,module.exports = XXX
其實(shí)也只是其中一行代碼,我們后面會(huì)講到,這行代碼的效果其實(shí)就是修改模塊里面的exports
屬性。比如我們?cè)賮?lái)一個(gè)c.js
:
// c.js let c = 1; c = c + 1; module.exports = c; c = 6;
在c.js
里面我們導(dǎo)出了一個(gè)c
,這個(gè)c
經(jīng)過(guò)了幾步計(jì)算,當(dāng)運(yùn)行到module.exports = c;
這行時(shí)c
的值為2
,所以我們require
的c.js
的值就是2
,后面將c
的值改為了6
并不影響前面的這行代碼:
const c = require('./c.js'); console.log(c); // c的值是2
前面c.js
的變量c
是一個(gè)基本數(shù)據(jù)類型,所以后面的c = 6;
不影響前面的module.exports
,那他如果是一個(gè)引用類型呢?我們直接來(lái)試試吧:
// d.js let d = { num: 1 }; d.num++; module.exports = d; d.num = 6;
然后在index.js
里面require
他:
const d = require('./d.js'); console.log(d); // { num: 6 }
我們發(fā)現(xiàn)在module.exports
后面給d.num
賦值仍然生效了,因?yàn)?code>d是一個(gè)對(duì)象,是一個(gè)引用類型,我們可以通過(guò)這個(gè)引用來(lái)修改他的值。其實(shí)對(duì)于引用類型來(lái)說(shuō),不僅僅在module.exports
后面可以修改他的值,在模塊外面也可以修改,比如index.js
里面就可以直接改:
const d = require('./d.js'); d.num = 7; console.log(d); // { num: 7 }
require
和module.exports
不是黑魔法我們通過(guò)前面的例子可以看出來(lái),require
和module.exports
干的事情并不復(fù)雜,我們先假設(shè)有一個(gè)全局對(duì)象{}
,初始情況下是空的,當(dāng)你require
某個(gè)文件時(shí),就將這個(gè)文件拿出來(lái)執(zhí)行,如果這個(gè)文件里面存在module.exports
,當(dāng)運(yùn)行到這行代碼時(shí)將module.exports
的值加入這個(gè)對(duì)象,鍵為對(duì)應(yīng)的文件名,最終這個(gè)對(duì)象就長(zhǎng)這樣:
{ "a.js": "hello world", "b.js": function add(){}, "c.js": 2, "d.js": { num: 2 } }
當(dāng)你再次require
某個(gè)文件時(shí),如果這個(gè)對(duì)象里面有對(duì)應(yīng)的值,就直接返回給你,如果沒(méi)有就重復(fù)前面的步驟,執(zhí)行目標(biāo)文件,然后將它的module.exports
加入這個(gè)全局對(duì)象,并返回給調(diào)用者。這個(gè)全局對(duì)象其實(shí)就是我們經(jīng)常聽(tīng)說(shuō)的緩存。所以require
和module.exports
并沒(méi)有什么黑魔法,就只是運(yùn)行并獲取目標(biāo)文件的值,然后加入緩存,用的時(shí)候拿出來(lái)用就行。再看看這個(gè)對(duì)象,因?yàn)?code>d.js是一個(gè)引用類型,所以你在任何地方獲取了這個(gè)引用都可以更改他的值,如果不希望自己模塊的值被更改,需要自己寫模塊時(shí)進(jìn)行處理,比如使用Object.freeze()
,Object.defineProperty()
之類的方法。
這一節(jié)的內(nèi)容都是一些概念,比較枯燥,但是也是我們需要了解的。
Node.js的模塊有好幾種類型,前面我們使用的其實(shí)都是文件模塊
,總結(jié)下來(lái),主要有這兩種類型:
- 內(nèi)置模塊:就是Node.js原生提供的功能,比如
fs
,http
等等,這些模塊在Node.js進(jìn)程起來(lái)時(shí)就加載了。- 文件模塊:我們前面寫的幾個(gè)模塊,還有第三方模塊,即
node_modules
下面的模塊都是文件模塊。
加載順序是指當(dāng)我們require(X)
時(shí),應(yīng)該按照什么順序去哪里找X
,在官方文檔上有詳細(xì)偽代碼,總結(jié)下來(lái)大概是這么個(gè)順序:
- 優(yōu)先加載內(nèi)置模塊,即使有同名文件,也會(huì)優(yōu)先使用內(nèi)置模塊。
- 不是內(nèi)置模塊,先去緩存找。
- 緩存沒(méi)有就去找對(duì)應(yīng)路徑的文件。
- 不存在對(duì)應(yīng)的文件,就將這個(gè)路徑作為文件夾加載。
- 對(duì)應(yīng)的文件和文件夾都找不到就去
node_modules
下面找。- 還找不到就報(bào)錯(cuò)了。
前面提到找不到文件就找文件夾,但是不可能將整個(gè)文件夾都加載進(jìn)來(lái),加載文件夾的時(shí)候也是有一個(gè)加載順序的:
- 先看看這個(gè)文件夾下面有沒(méi)有
package.json
,如果有就找里面的main
字段,main
字段有值就加載對(duì)應(yīng)的文件。所以如果大家在看一些第三方庫(kù)源碼時(shí)找不到入口就看看他package.json
里面的main
字段吧,比如jquery
的main
字段就是這樣:"main": "dist/jquery.js"
。- 如果沒(méi)有
package.json
或者package.json
里面沒(méi)有main
就找index
文件。- 如果這兩步都找不到就報(bào)錯(cuò)了。
require
主要支持三種文件類型:
- .js:
.js
文件是我們最常用的文件類型,加載的時(shí)候會(huì)先運(yùn)行整個(gè)JS文件,然后將前面說(shuō)的module.exports
作為require
的返回值。- .json:
.json
文件是一個(gè)普通的文本文件,直接用JSON.parse
將其轉(zhuǎn)化為對(duì)象返回就行。- .node:
.node
文件是C++編譯后的二進(jìn)制文件,純前端一般很少接觸這個(gè)類型。
require
前面其實(shí)我們已經(jīng)將原理講的七七八八了,下面來(lái)到我們的重頭戲,自己實(shí)現(xiàn)一個(gè)require
。實(shí)現(xiàn)require
其實(shí)就是實(shí)現(xiàn)整個(gè)Node.js的模塊加載機(jī)制,我們?cè)賮?lái)理一下需要解決的問(wèn)題:
- 通過(guò)傳入的路徑名找到對(duì)應(yīng)的文件。
- 執(zhí)行找到的文件,同時(shí)要注入
module
和require
這些方法和屬性,以便模塊文件使用。- 返回模塊的
module.exports
本文的手寫代碼全部參照Node.js官方源碼,函數(shù)名和變量名盡量保持一致,其實(shí)就是精簡(jiǎn)版的源碼,大家可以對(duì)照著看,寫到具體方法時(shí)我也會(huì)貼上對(duì)應(yīng)的源碼地址??傮w的代碼都在這個(gè)文件里面:https://github.com/nodejs/node/blob/c6b96895cc74bc6bd658b4c6d5ea152d6e686d20/lib/internal/modules/cjs/loader.js
Node.js模塊加載的功能全部在Module
類里面,整個(gè)代碼使用面向?qū)ο蟮乃枷?,如果你?duì)JS的面向?qū)ο筮€不是很熟悉可以先看看這篇文章。Module
類的構(gòu)造函數(shù)也不復(fù)雜,主要是一些值的初始化,為了跟官方Module
名字區(qū)分開(kāi),我們自己的類命名為MyModule
:
function MyModule(id = '') { this.id = id; // 這個(gè)id其實(shí)就是我們r(jià)equire的路徑 this.path = path.dirname(id); // path是Node.js內(nèi)置模塊,用它來(lái)獲取傳入?yún)?shù)對(duì)應(yīng)的文件夾路徑 this.exports = {}; // 導(dǎo)出的東西放這里,初始化為空對(duì)象 this.filename = null; // 模塊對(duì)應(yīng)的文件名 this.loaded = false; // loaded用來(lái)標(biāo)識(shí)當(dāng)前模塊是否已經(jīng)加載 }
我們一直用的require
其實(shí)是Module
類的一個(gè)實(shí)例方法,內(nèi)容很簡(jiǎn)單,先做一些參數(shù)檢查,然后調(diào)用Module._load
方法,源碼看這里:/tupian/20230522/loader.js class="brush:js;toolbar:false">MyModule.prototype.require = function (id) {
return Module._load(id);
}
MyModule._load
是一個(gè)靜態(tài)方法,這才是require
方法的真正主體,他干的事情其實(shí)是:
- 先檢查請(qǐng)求的模塊在緩存中是否已經(jīng)存在了,如果存在了直接返回緩存模塊的
exports
。- 如果不在緩存中,就
new
一個(gè)Module
實(shí)例,用這個(gè)實(shí)例加載對(duì)應(yīng)的模塊,并返回模塊的exports
。
我們自己來(lái)實(shí)現(xiàn)下這兩個(gè)需求,緩存直接放在Module._cache
這個(gè)靜態(tài)變量上,這個(gè)變量官方初始化使用的是Object.create(null)
,這樣可以使創(chuàng)建出來(lái)的原型指向null
,我們也這樣做吧:
MyModule._cache = Object.create(null); MyModule._load = function (request) { // request是我們傳入的路勁參數(shù) const filename = MyModule._resolveFilename(request); // 先檢查緩存,如果緩存存在且已經(jīng)加載,直接返回緩存 const cachedModule = MyModule._cache[filename]; if (cachedModule !== undefined) { return cachedModule.exports; } // 如果緩存不存在,我們就加載這個(gè)模塊 // 加載前先new一個(gè)MyModule實(shí)例,然后調(diào)用實(shí)例方法load來(lái)加載 // 加載完成直接返回module.exports const module = new MyModule(filename); // load之前就將這個(gè)模塊緩存下來(lái),這樣如果有循環(huán)引用就會(huì)拿到這個(gè)緩存,但是這個(gè)緩存里面的exports可能還沒(méi)有或者不完整 MyModule._cache[filename] = module; module.load(filename); return module.exports; }
上述代碼對(duì)應(yīng)的源碼看這里:/tupian/20230522/loader.js id="item-4-9">MyModule._resolveFilename
MyModule._resolveFilename
從名字就可以看出來(lái),這個(gè)方法是通過(guò)用戶傳入的require
參數(shù)來(lái)解析到真正的文件地址的,源碼中這個(gè)方法比較復(fù)雜,因?yàn)榘凑涨懊嬷v的,他要支持多種參數(shù):內(nèi)置模塊,相對(duì)路徑,絕對(duì)路徑,文件夾和第三方模塊等等,如果是文件夾或者第三方模塊還要解析里面的package.json
和index.js
。我們這里主要講原理,所以我們就只實(shí)現(xiàn)通過(guò)相對(duì)路徑和絕對(duì)路徑來(lái)查找文件,并支持自動(dòng)添加js
和json
兩種后綴名:
MyModule._resolveFilename = function (request) { const filename = path.resolve(request); // 獲取傳入?yún)?shù)對(duì)應(yīng)的絕對(duì)路徑 const extname = path.extname(request); // 獲取文件后綴名 // 如果沒(méi)有文件后綴名,嘗試添加.js和.json if (!extname) { const exts = Object.keys(MyModule._extensions); for (let i = 0; i < exts.length; i++) { const currentPath = `${filename}${exts[i]}`; // 如果拼接后的文件存在,返回拼接的路徑 if (fs.existsSync(currentPath)) { return currentPath; } } } return filename; }
上述源碼中我們還用到了一個(gè)靜態(tài)變量MyModule._extensions
,這個(gè)變量是用來(lái)存各種文件對(duì)應(yīng)的處理方法的,我們后面會(huì)實(shí)現(xiàn)他。
MyModule._resolveFilename
對(duì)應(yīng)的源碼看這里:/tupian/20230522/loader.js id="item-4-10">MyModule.prototype.load
MyModule.prototype.load
是一個(gè)實(shí)例方法,這個(gè)方法就是真正用來(lái)加載模塊的方法,這其實(shí)也是不同類型文件加載的一個(gè)入口,不同類型的文件會(huì)對(duì)應(yīng)MyModule._extensions
里面的一個(gè)方法:
MyModule.prototype.load = function (filename) { // 獲取文件后綴名 const extname = path.extname(filename); // 調(diào)用后綴名對(duì)應(yīng)的處理函數(shù)來(lái)處理 MyModule._extensions[extname](this, filename); this.loaded = true; }
注意這段代碼里面的this
指向的是module
實(shí)例,因?yàn)樗且粋€(gè)實(shí)例方法。對(duì)應(yīng)的源碼看這里: /tupian/20230522/loader.js id="item-4-11">加載js文件: MyModule._extensions['.js']
前面我們說(shuō)過(guò)不同文件類型的處理方法都掛載在MyModule._extensions
上面的,我們先來(lái)實(shí)現(xiàn).js
類型文件的加載:
MyModule._extensions['.js'] = function (module, filename) { const content = fs.readFileSync(filename, 'utf8'); module._compile(content, filename); }
可以看到js
的加載方法很簡(jiǎn)單,只是把文件內(nèi)容讀出來(lái),然后調(diào)了另外一個(gè)實(shí)例方法_compile
來(lái)執(zhí)行他。對(duì)應(yīng)的源碼看這里:/tupian/20230522/loader.js id="item-4-12">編譯執(zhí)行js文件:MyModule.prototype._compile
MyModule.prototype._compile
是加載JS文件的核心所在,也是我們最常使用的方法,這個(gè)方法需要將目標(biāo)文件拿出來(lái)執(zhí)行一遍,執(zhí)行之前需要將它整個(gè)代碼包裹一層,以便注入exports, require, module, __dirname, __filename
,這也是我們能在JS文件里面直接使用這幾個(gè)變量的原因。要實(shí)現(xiàn)這種注入也不難,假如我們require
的文件是一個(gè)簡(jiǎn)單的Hello World
,長(zhǎng)這樣:
module.exports = "hello world";
那我們?cè)趺磥?lái)給他注入module
這個(gè)變量呢?答案是執(zhí)行的時(shí)候在他外面再加一層函數(shù),使他變成這樣:
function (module) { // 注入module變量,其實(shí)幾個(gè)變量同理 module.exports = "hello world"; }
所以我們?nèi)绻麑⑽募?nèi)容作為一個(gè)字符串的話,為了讓他能夠變成上面這樣,我們需要再給他拼接上開(kāi)頭和結(jié)尾,我們直接將開(kāi)頭和結(jié)尾放在一個(gè)數(shù)組里面:
MyModule.wrapper = [ '(function (exports, require, module, __filename, __dirname) { ', '\n});' ];
注意我們拼接的開(kāi)頭和結(jié)尾多了一個(gè)()
包裹,這樣我們后面可以拿到這個(gè)匿名函數(shù),在后面再加一個(gè)()
就可以傳參數(shù)執(zhí)行了。然后將需要執(zhí)行的函數(shù)拼接到這個(gè)方法中間:
MyModule.wrap = function (script) { return MyModule.wrapper[0] + script + MyModule.wrapper[1]; };
這樣通過(guò)MyModule.wrap
包裝的代碼就可以獲取到exports, require, module, __filename, __dirname
這幾個(gè)變量了。知道了這些就可以來(lái)寫MyModule.prototype._compile
了:
MyModule.prototype._compile = function (content, filename) { const wrapper = Module.wrap(content); // 獲取包裝后函數(shù)體 // vm是nodejs的虛擬機(jī)沙盒模塊,runInThisContext方法可以接受一個(gè)字符串并將它轉(zhuǎn)化為一個(gè)函數(shù) // 返回值就是轉(zhuǎn)化后的函數(shù),所以compiledWrapper是一個(gè)函數(shù) const compiledWrapper = vm.runInThisContext(wrapper, { filename, lineOffset: 0, displayErrors: true, }); // 準(zhǔn)備exports, require, module, __filename, __dirname這幾個(gè)參數(shù) // exports可以直接用module.exports,即this.exports // require官方源碼中還包裝了一層,其實(shí)最后調(diào)用的還是this.require // module不用說(shuō),就是this了 // __filename直接用傳進(jìn)來(lái)的filename參數(shù)了 // __dirname需要通過(guò)filename獲取下 const dirname = path.dirname(filename); compiledWrapper.call(this.exports, this.exports, this.require, this, filename, dirname); }
上述代碼要注意我們注入進(jìn)去的幾個(gè)參數(shù)和通過(guò)call
傳進(jìn)去的this
:
- this:
compiledWrapper
是通過(guò)call
調(diào)用的,第一個(gè)參數(shù)就是里面的this
,這里我們傳入的是this.exports
,也就是module.exports
,也就是說(shuō)我們js
文件里面this
是對(duì)module.exports
的一個(gè)引用。- exports:
compiledWrapper
正式接收的第一個(gè)參數(shù)是exports
,我們傳的也是this.exports
,所以js
文件里面的exports
也是對(duì)module.exports
的一個(gè)引用。- require: 這個(gè)方法我們傳的是
this.require
,其實(shí)就是MyModule.prototype.require
,也就是MyModule._load
。- module: 我們傳入的是
this
,也就是當(dāng)前模塊的實(shí)例。- __filename:文件所在的絕對(duì)路徑。
- __dirname: 文件所在文件夾的絕對(duì)路徑。
到這里,我們的JS文件其實(shí)已經(jīng)記載完了,對(duì)應(yīng)的源碼看這里:/tupian/20230522/loader.js id="item-4-13">加載json文件: MyModule._extensions['.json']
加載json
文件就簡(jiǎn)單多了,只需要將文件讀出來(lái)解析成json
就行了:
MyModule._extensions['.json'] = function (module, filename) { const content = fs.readFileSync(filename, 'utf8'); module.exports = JSONParse(content); }
exports
和module.exports
的區(qū)別網(wǎng)上經(jīng)常有人問(wèn),node.js
里面的exports
和module.exports
到底有什么區(qū)別,其實(shí)前面我們的手寫代碼已經(jīng)給出答案了,我們這里再就這個(gè)問(wèn)題詳細(xì)講解下。exports
和module.exports
這兩個(gè)變量都是通過(guò)下面這行代碼注入的。
compiledWrapper.call(this.exports, this.exports, this.require, this, filename, dirname);
初始狀態(tài)下,exports === module.exports === {}
,exports
是module.exports
的一個(gè)引用,如果你一直是這樣使用的:
exports.a = 1; module.exports.b = 2; console.log(exports === module.exports); // true
上述代碼中,exports
和module.exports
都是指向同一個(gè)對(duì)象{}
,你往這個(gè)對(duì)象上添加屬性并沒(méi)有改變這個(gè)對(duì)象本身的引用地址,所以exports === module.exports
一直成立。
但是如果你哪天這樣使用了:
exports = { a: 1 }
或者這樣使用了:
module.exports = { b: 2 }
那其實(shí)你是給exports
或者module.exports
重新賦值了,改變了他們的引用地址,那這兩個(gè)屬性的連接就斷開(kāi)了,他們就不再相等了。需要注意的是,你對(duì)module.exports
的重新賦值會(huì)作為模塊的導(dǎo)出內(nèi)容,但是你對(duì)exports
的重新賦值并不能改變模塊導(dǎo)出內(nèi)容,只是改變了exports
這個(gè)變量而已,因?yàn)槟K始終是module
,導(dǎo)出內(nèi)容是module.exports
。
Node.js對(duì)于循環(huán)引用是進(jìn)行了處理的,下面是官方例子:
a.js
:
console.log('a 開(kāi)始'); exports.done = false; const b = require('./b.js'); console.log('在 a 中,b.done = %j', b.done); exports.done = true; console.log('a 結(jié)束');
b.js
:
console.log('b 開(kāi)始'); exports.done = false; const a = require('./a.js'); console.log('在 b 中,a.done = %j', a.done); exports.done = true; console.log('b 結(jié)束');
main.js
:
console.log('main 開(kāi)始'); const a = require('./a.js'); const b = require('./b.js'); console.log('在 main 中,a.done=%j,b.done=%j', a.done, b.done);
當(dāng) main.js
加載 a.js
時(shí), a.js
又加載 b.js
。 此時(shí), b.js
會(huì)嘗試去加載 a.js
。 為了防止無(wú)限的循環(huán),會(huì)返回一個(gè) a.js
的 exports
對(duì)象的 未完成的副本 給 b.js
模塊。 然后 b.js
完成加載,并將 exports
對(duì)象提供給 a.js
模塊。
那么這個(gè)效果是怎么實(shí)現(xiàn)的呢?答案就在我們的MyModule._load
源碼里面,注意這兩行代碼的順序:
MyModule._cache[filename] = module; module.load(filename);
上述代碼中我們是先將緩存設(shè)置了,然后再執(zhí)行的真正的load
,順著這個(gè)思路我能來(lái)理一下這里的加載流程:
main
加載a
,a
在真正加載前先去緩存中占一個(gè)位置a
在正式加載時(shí)加載了b
b
又去加載了a
,這時(shí)候緩存中已經(jīng)有a
了,所以直接返回a.exports
,即使這時(shí)候的exports
是不完整的。
require
不是黑魔法,整個(gè)Node.js的模塊加載機(jī)制都是JS
實(shí)現(xiàn)的。exports, require, module, __filename, __dirname
五個(gè)參數(shù)都不是全局變量,而是模塊加載的時(shí)候注入的。vm
來(lái)實(shí)現(xiàn)。this, exports, module.exports
都指向同一個(gè)對(duì)象,如果你對(duì)他們重新賦值,這種連接就斷了。module.exports
的重新賦值會(huì)作為模塊的導(dǎo)出內(nèi)容,但是你對(duì)exports
的重新賦值并不能改變模塊導(dǎo)出內(nèi)容,只是改變了exports
這個(gè)變量而已,因?yàn)槟K始終是module
,導(dǎo)出內(nèi)容是module.exports
。exports
。感謝各位的閱讀!關(guān)于Node.js的模塊加載機(jī)制是什么就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!
新聞名稱:Node.js的模塊加載機(jī)制是什么-創(chuàng)新互聯(lián)
本文網(wǎng)址:http://aaarwkj.com/article10/dppedo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站制作、軟件開(kāi)發(fā)、網(wǎng)頁(yè)設(shè)計(jì)公司、網(wǎng)站改版、全網(wǎng)營(yíng)銷推廣、品牌網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容