成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Node.js模塊化你所需要知道的事

開發 前端
今天我們來聊一聊Node.js模塊化你所需要知道的一些事兒,一探Node.js模塊化的面貌。

[[361143]]

 前言

我們知道,Node.js是基于CommonJS規范進行模塊化管理的,模塊化是面對復雜的業務場景不可或缺的工具,或許你經常使用它,但卻從沒有系統的了解過,所以今天我們來聊一聊Node.js模塊化你所需要知道的一些事兒,一探Node.js模塊化的面貌。

正文

在Node.js中,內置了兩個模塊來進行模塊化管理,這兩個模塊也是兩個我們非常熟悉的關鍵字:require和module。內置意味著我們可以在全局范圍內使用這兩個模塊,而無需像其他模塊一樣,需要先引用再使用。 

  1. 無需 require('require') or require('module')  
  2. 復制代碼 

在Node.js中引用一個模塊并不是什么難事兒,很簡單: 

  1. const config = require('/path/to/file')  
  2. 復制代碼 

但實際上,這句簡單的代碼執行了一共五個步驟:

了解這五個步驟有助于我們了解Node.js模塊化的基本原理,也能讓我們甄別一些陷阱,讓我們簡單概括下這五個步驟都做了什么:

  •  Resolving: 找到待引用的目標模塊,并生成絕對路徑。
  •  Loading: 判斷待引用的模塊內容是什么類型,它可能是.json文件、.js文件或者.node文件。
  •  Wrapping: 顧名思義,包裝被引用的模塊。通過包裝,讓模塊具有私有作用域。
  •  Evaluating: 被加載的模塊被真正的解析和處理執行。
  •  Caching: 緩存模塊,這讓我們在引入相同模塊時,不用再重復上述步驟。

有些同學看完這五個步驟可能已經心知肚明,對這些原理輕車熟路,有些同學心中可能產生了更多疑惑,無論如何,接下來的內容會詳細解析上述的執行步驟,希望能幫助大家答疑解惑 or 鞏固知識、查缺補漏。

By the way,如果有需要,可以和我一樣,構建一個實驗目錄,跟著Demo進行實驗。

什么是模塊

想要了解模塊化,需要先直觀地看看模塊是什么。

我們知道在Node.js中,文件即模塊,剛剛提到了模塊可以是.js、.json或者.node文件,通過引用它們,可以獲取工具函數、變量、配置等等,但是它的具體結構是怎樣呢?在命令行中簡單執行下面的命令就可以看到模塊,也就是module對象的結構: 

  1. ~/learn-node $ node  
  2. > module  
  3. Module {  
  4.   id: '<repl>',  
  5.   exports: {},  
  6.   parent: undefined,  
  7.   filename: null,  
  8.   loaded: false,  
  9.   children: [],  
  10.   paths: [ ... ] }  
  11. 復制代碼 

可以看到模塊也就是一個普通對象,只不過結構中有幾個特殊的屬性值,需要我們一一去理解,有些屬性,例如id、parent、filename、children甚至都無需解釋,通過字面意思就可以理解。

后續的內容會幫助大家理解這些字段的意義和作用。

Resolving

大致了解了什么是模塊后,我們從第一個步驟Resolving開始,了解模塊化原理,也就是Node.js如何尋找目標模塊,并生成目標模塊的絕對路徑。

那么什么我們剛剛要先打印module對象,先讓大家了解module的結構呢?因為這里有兩個字段值id、paths和Resolving這個步驟息息相關。一起來看看吧。

  •  首先是id屬性:

每個module都有id屬性,通常這個屬性值是模塊的完整路徑,通過這個值Node.js可以標識和定位模塊的所在位置。但是在這兒并沒有具體的模塊,我們只是在命令行中輸出了module的結構,所以為默認的<repl>值(repl表示交互式解釋器)。

  •  其次是paths屬性:

這個paths屬性有什么作用呢?Node.js允許我們用多種方式來引用模塊,比如相對路徑、絕對路徑、預置路徑(馬上會解釋),假設我們需要引用一個叫做find-me的模塊,require如何幫助我們找到這個模塊呢? 

  1. require('find-me')  
  2. 復制代碼 

我們先打印看看paths中是什么內容: 

  1. ~/learn-node $ node  
  2. > module.paths  
  3. [ '/Users/samer/learn-node/repl/node_modules',  
  4.   '/Users/samer/learn-node/node_modules',  
  5.   '/Users/samer/node_modules',  
  6.   '/Users/node_modules',  
  7.   '/node_modules',  
  8.   '/Users/samer/.node_modules',  
  9.   '/Users/samer/.node_libraries',  
  10.   '/usr/local/Cellar/node/7.7.1/lib/node' ]  
  11. 復制代碼 

ok,其實就是一堆系統絕對路徑,這些路徑表示了所有目標模塊可能出現的位置,并且它們是有序的,這意味著Node.js會按序查找paths中列出的所有路徑,如果找到這個模塊,就輸出該模塊的絕對路徑供后續使用。

現在我們知道Node.js會在這一堆目錄中查找module,嘗試執行require('find-me')來查找find-me模塊,由于我們并沒有在任何目錄放置find-me模塊,所以Node.js在遍歷所有目錄之后并不能找到目標模塊,因此報錯Cannot find module 'find-me',這個錯誤大家也許經??吹剑?nbsp;

  1. ~/learn-node $ node  
  2. > require('find-me')  
  3. Error: Cannot find module 'find-me'  
  4.     at Function.Module._resolveFilename (module.js:470:15)  
  5.     at Function.Module._load (module.js:418:25)  
  6.     at Module.require (module.js:498:17)  
  7.     at require (internal/module.js:20:19)  
  8.     at repl:1:1  
  9.     at ContextifyScript.Script.runInThisContext (vm.js:23:33)  
  10.     at REPLServer.defaultEval (repl.js:336:29)  
  11.     at bound (domain.js:280:14)  
  12.     at REPLServer.runBound [as eval] (domain.js:293:12)  
  13.     at REPLServer.onLine (repl.js:533:10)  
  14. 復制代碼 

現在,可以嘗試把需要引用的find-me模塊放在上述的任意一個目錄下,在這里我們創建一個node_modules目錄,并創建find-me.js文件,讓Node.js能夠找到它: 

  1. ~/learn-node $ mkdir node_modules   
  2. ~/learn-node $ echo "console.log('I am not lost');" > node_modules/find-me.js  
  3. ~/learn-node $ node  
  4. > require('find-me');  
  5. I am not lost  
  6. {}  
  7. >  
  8. 復制代碼 

手動創建了find-me.js文件后,Node.js果然找到了目標模塊。當然,當Node.js本地的node_modules目錄中找到了find-me模塊,就不會再去后續的目錄中繼續尋找了。

有Node.js開發經驗的同學會發現在引用模塊時,不一定非得指定到準確的文件,也可以通過引用目錄來完成對目標模塊的引用,例如: 

  1. ~/learn-node $ mkdir -p node_modules/find-me  
  2. ~/learn-node $ echo "console.log('Found again.');" > node_modules/find-me/index.js  
  3. ~/learn-node $ node  
  4. > require('find-me');  
  5. Found again.  
  6. {}  
  7. >  
  8. 復制代碼 

find-me目錄下的index.js文件會被自動引入。

當然,這是有規則限制的,Node.js之所以能夠找到find-me目錄下的index.js文件,是因為默認的模塊引入規則是當具體的文件名缺失時尋找index.js文件。我們也可以更改引入規則(通過修改package.json),比如把index \-> main: 

  1. ~/learn-node $ echo "console.log('I rule');" > node_modules/find-me/main.js  
  2. ~/learn-node $ echo '{ "name": "find-me-folder", "main": "main.js" }' > node_modules/find-me/package.json  
  3. ~/learn-node $ node  
  4. > require('find-me'); 
  5. I rule  
  6. {}  
  7. > 
  8. 復制代碼 

require.resolve

如果你只想要在項目中引入某個模塊,而不想立即執行它,可以使用require.resolve方法,它和require方法功能相似,只是并不會執行被引入的模塊方法: 

  1. > require.resolve('find-me');  
  2. '/Users/samer/learn-node/node_modules/find-me/start.js'  
  3. > require.resolve('not-there');  
  4. Error: Cannot find module 'not-there'  
  5.     at Function.Module._resolveFilename (module.js:470:15)  
  6.     at Function.resolve (internal/module.js:27:19)  
  7.     at repl:1:9  
  8.     at ContextifyScript.Script.runInThisContext (vm.js:23:33)  
  9.     at REPLServer.defaultEval (repl.js:336:29)  
  10.     at bound (domain.js:280:14)  
  11.     at REPLServer.runBound [as eval] (domain.js:293:12)  
  12.     at REPLServer.onLine (repl.js:533:10)  
  13.     at emitOne (events.js:101:20)  
  14.     at REPLServer.emit (events.js:191:7)  
  15. >  
  16. 復制代碼 

可以看到,如果該模塊被找到了,Node.js會打印模塊的完整路徑,如果未找到,就報錯。

了解了Node.js是如何尋找模塊之后,來看看Node.js是如何加載模塊的。

模塊間的父子依賴關系

我們把模塊間引用關系,表示為父子依賴關系。

簡單創建一個lib/util.js文件,添加一行console.log語句,標識這是一個被引用的子模塊。 

  1. ~/learn-node $ mkdir lib  
  2. ~/learn-node $ echo "console.log('In util');" > lib/util.js  
  3. 復制代碼 

在index.js也輸入一行console.log語句,標識這是一個父模塊,并引用剛剛創建的lib/util.js作為子模塊。 

  1. ~/learn-node $ echo "require('./lib/util'); console.log('In index, parent', module);" > index.js  
  2. 復制代碼 

執行index.js,看看它們間的依賴關系: 

  1. ~/learn-node $ node index.js  
  2. In util  
  3. In index <ref *1> Module {  
  4.   id: '.',  
  5.   path: '/Users/samer/',  
  6.   exports: {},  
  7.   parent: null,  
  8.   filename: '/Users/samer/index.js',  
  9.   loaded: false, 
  10.    children: [ 
  11.      Module {  
  12.       id: '/Users/samer/lib/util.js',  
  13.       path: '/Users/samer/lib', 
  14.        exports: {},  
  15.       parent: [Circular *1],  
  16.       filename: '/Users/samer/lib/util.js',  
  17.       loaded: true,  
  18.       children: [],  
  19.       paths: [Array]  
  20.     }  
  21.   ],  
  22.   paths: [...]  
  23.  
  24. 復制代碼 

在這里我們關注與依賴關系相關的兩個屬性:children和parent。

在打印的結果中,children字段包含了被引入的util.js模塊,這表明了util.js是index.js所依賴的子模塊。

但仔細觀察util.js模塊的parent屬性,發現這里出現了Circular這個值,原因是當我們打印模塊信息時,產生了循環的依賴關系,在子模塊信息中打印父模塊信息,又要在父模塊信息中打印子模塊信息,所以Node.js簡單地將它處理標記為Circular。

為什么需要了解父子依賴關系呢?因為這關系到Node.js是如何處理循環依賴關系的,后續會詳細描述。

在看循環依賴關系的處理問題之前,我們需要先了解兩個關鍵的概念:exports和module.exports。

exports, module.exports

  • exports:

exports是一個特殊的對象,它在Node.js中可以無需聲明,作為全局變量直接使用。它實際上是module.exports的引用,通過修改exports可以達到修改module.exports的目的。

exports也是剛剛打印的module結構中的一個屬性值,但是剛剛打印出來的值都是空對象,因為我們并沒有在文件中對它進行操作,現在我們可以嘗試簡單地為它賦值: 

  1. // 在lib/util.js的開頭新增一行  
  2. exports.id = 'lib/util' 
  3. // 在index.js的開頭新增一行  
  4. exports.id = 'index' 
  5. 復制代碼 

執行index.js: 

  1. ~/learn-node $ node index.js  
  2. In index Module {  
  3.   id: '.', 
  4.    exports: { id: 'index' },  
  5.   loaded: false,  
  6.   ... }  
  7. In util Module {  
  8.   id: '/Users/samer/learn-node/lib/util.js',  
  9.   exports: { id: 'lib/util' },  
  10.   parent:  
  11.    Module {  
  12.      id: '.',  
  13.      exports: { id: 'index' },  
  14.      loaded: false,  
  15.      ... },  
  16.   loaded: false,  
  17.   ... } 
  18.  復制代碼 

可以看到剛剛添加的兩個id屬性被成功添加到exports對象中。我們也可以添加除id以外的任意屬性,就像操作普通對象一樣,當然也可以把exports變成一個function,例如: 

  1. exports = function() {}  
  2. 復制代碼 
  •  module.exports:

module.exports對象其實就是我們最終通過require所得到的東西。我們在編寫一個模塊時,最終給module.exports賦什么值,其他人引用該模塊時就能得到什么值。例如,結合剛剛對lib/util的操作: 

  1. const util = require('./lib/util');  
  2. console.log('UTIL:', util);  
  3. // 輸出結果 
  4. UTIL: { id: 'lib/util' }  
  5. 復制代碼 

由于我們剛剛通過exports對象為module.exports賦值{id: 'lib/util'},因此require的結果就相應地發生了變化。

現在我們大致了解了exports和module.exports都是什么,但是有一個小細節需要注意,那就是Node.js的模塊加載是個同步的過程。

我們回過頭來看看module結構中的loaded屬性,這個屬性標識這個模塊是否被加載完成,通過這個屬性就能簡單驗證Node.js模塊加載的同步性。

當模塊被加載完成后,loaded值應該為true。但到目前為止每次我們打印module時,它的狀態都是false,這其實正是因為在Node.js中,模塊的加載是同步的,當我們還未完成加載的動作(加載的動作包括對module進行標記,包括標記loaded屬性),因此打印出的結果就是默認的loaded: false。

我們用setImmediate來幫助我們驗證這個信息: 

  1. // In index.js  
  2. setImmediate(() => {  
  3.   console.log('The index.js module object is now loaded!', module)  
  4. });  
  5. 復制代碼  
  1. The index.js module object is now loaded! Module {  
  2.   id: '.',  
  3.   exports: [Function],  
  4.   parent: null,  
  5.   filename: '/Users/samer/learn-node/index.js',  
  6.   loaded: true,  
  7.   children:  
  8.    [ Module {  
  9.        id: '/Users/samer/learn-node/lib/util.js',  
  10.        exports: [Object], 
  11.         parent: [Circular],  
  12.        filename: '/Users/samer/learn-node/lib/util.js',  
  13.        loaded: true,  
  14.        children: [],  
  15.        paths: [Object] } ],  
  16.   paths:  
  17.    [ '/Users/samer/learn-node/node_modules',  
  18.      '/Users/samer/node_modules',  
  19.      '/Users/node_modules',  
  20.      '/node_modules' ] }  
  21. 復制代碼 

ok,由于console.log被后置到加載完成(打完標記)之后,因此現在加載狀態變成了loaded: true。這充分驗證了Node.js模塊加載是一個同步過程。

了解了exports、module.exports以及模塊加載的同步性后,來看看Node.js是如何處理模塊的循環依賴關系。

模塊循環依賴

在上述內容中,我們了解到了模塊之間是存在父子依賴關系的,那如果模塊之間產生了循環的依賴關系,Node.js會怎么處理呢?假設有兩個模塊,分別為module1.js和modole2.js,并且它們互相引用了對方,如下: 

  1. // lib/module1.js  
  2. exports.a = 1
  3. require('./module2'); // 在這兒引用  
  4. exports.b = 2 
  5. exports.c = 3 
  6. // lib/module2.js  
  7. const Module1 = require('./module1');  
  8. console.log('Module1 is partially loaded here', Module1); // 引用module1并打印它  
  9. 復制代碼 

嘗試運行module1.js,可以看到輸出結果: 

  1. ~/learn-node $ node lib/module1.js  
  2. Module1 is partially loaded here { a: 1 }  
  3. 復制代碼 

結果中只輸出了{a: 1},而{b: 2, c: 3}卻不見了。仔細觀察module1.js,發現我們在module1.js的中間位置添加了對module2.js的引用,也就是exports.b = 2和exports.c = 3還未執行之前的位置。如果我們把這個位置稱作發生循環依賴的位置,那么我們得到的結果就是在循環依賴發生前被導出的屬性,這也是基于我們上述驗證過的Node.js的模塊加載是同步過程的結論。

Node.js就是這樣簡單地處理循環依賴。在加載模塊的過程中,會逐步構建exports對象,為exports賦值。如果我們在模塊被完全加載前就引用這個模塊,那么我們只能得到部分的exports對象屬性。

.json和.node

在Node.js中,我們不僅能用require來引用JavaScript文件,還能用于引用JSON或C++插件(.json和.node文件)。我們甚至都不需要顯式地聲明對應的文件后綴。

在命令行中也可以看到require所支持的文件類型: 

  1. ~ % node  
  2. > require.extensions  
  3. [Object: null prototype] {  
  4.   '.js': [Function (anonymous)],  
  5.   '.json': [Function (anonymous)],  
  6.   '.node': [Function (anonymous)]  
  7.  
  8. 復制代碼 

當我們用require引用一個模塊,首先Node.js會去匹配是否有.js文件,如果沒有找到,再去匹配.json文件,如果還沒找到,最后再嘗試匹配.node文件。但是通常情況下,為了避免混淆和引用意圖不明,可以遵循在引用.json或.node文件時顯式地指定后綴,引用.js時省略后綴(可選,或都加上后綴)。

  •  .json文件:

引用.json文件很常用,例如一些項目中的靜態配置,使用.json文件來存儲更便于管理,例如: 

  1.  
  2.   "host": "localhost",  
  3.   "port": 8080  
  4.  
  5. 復制代碼 

引用它或使用它都很簡單: 

  1. const { host, port } = require('./config');  
  2. console.log(`Server will run at http://${host}:${port}`)  
  3. 復制代碼 

輸出如下: 

  1. Server will run at http://localhost:8080  
  2. 復制代碼 
  •  .node文件:

.node文件是由C++文件轉化而來,官網提供了一個簡單的由C++實現的 hello插件 ,它暴露了一個hello()方法,輸出字符串world。有需要的話,可以跳轉鏈接做更多了解并進行實驗。

我們可以通過node-gyp來將.cc文件編譯和構建成.node文件,過程也非常簡單,只需要配置一個binding.gyp文件即可。這里不詳細闡述,只需要知道生成.node文件后,就可以正常地引用該文件,并使用其中的方法。

例如,將hello()轉化生成addon.node文件后,引用并使用它: 

  1. const addon = require('./addon');  
  2. console.log(addon.hello());  
  3. 復制代碼 

Wrapping

其實在上述內容中,我們闡述了在Node.js中引用一個模塊的前兩個步驟Resolving和Loading,它們分別解決了模塊的路徑和加載的問題。接下來看看Wrapping都做了什么。

Wrapping就是包裝,包裝的對象就是所有我們在模塊中寫的代碼。也就是我們引用模塊時,其實經歷了一層『透明』的包裝。

要了解這個包裝過程,首先要理解exports和module.exports之間的區別。

exports是對module.exports的引用,我們可以在模塊中使用exports來導出屬性,但是不能直接替換它。例如: 

  1. exports.id = 42; // ok,此時exports指向module.exports,相當于修改了module.exports.  
  2. exports = { id: 42 }; // 無用,只是將它指向了{ id: 42 }對象而已,對module.exports不會產生實際改變.  
  3. module.exports = { id: 42 }; // ok,直接操作module.exports.  
  4. 復制代碼 

大家也許會有疑惑,為什么這個exports對象似乎對每個模塊來說都是一個全局對象,但是它又能夠區分導出的對象是來自于哪個模塊,這是怎么做到的。

在了解包裝(Wrapping)過程之前,來看一個小例子: 

  1. // In a.js  
  2. var value = 'global'  
  3. // In b.js  
  4. console.log(value) // 輸出:global  
  5. // In c.js 
  6. console.log(value) // 輸出:global  
  7. // In index.html  
  8. ...  
  9. <script src="a.js"></script>  
  10. <script src="b.js"></script>  
  11. <script src="c.js"></script>  
  12. 復制代碼 

當我們在a.js腳本中定義一個值value,這個值是全局可見的,后續引入的b.js和c.js都是可以訪問該value值。但是在Node.js模塊中卻并不是這樣,在一個模塊中定義的變量具有私有作用域,在其它模塊中無法直接訪問。這個私有作用域如何產生的?

答案很簡單,是因為在編譯模塊之前,Node.js將模塊中的內容包裝在了一個function中,通過函數作用域實現了私有作用域。

通過require('module').wrapper可以打印出wrapper屬性: 

  1. ~ $ node  
  2. > require('module').wrapper  
  3. [ '(function (exports, require, module, __filename, __dirname) { ',  
  4.   '\n});' ]  
  5. >  
  6. 復制代碼 

Node.js不會直接執行文件中的任何代碼,但它會通過這個包裝后的function來執行代碼,這讓我們的每個模塊都有了私有作用域,不會互相影響。

這個包裝函數有五個參數:exports, require, module, __filename, __dirname。我們可以通過arguments參數直接訪問和打印這些參數: 

  1. ~/learn-node $ echo "console.log(arguments)" > index.js  
  2. ~/learn-node $ node index.js  
  3. { '0': {},  
  4.   '1':  
  5.    { [Function: require]  
  6.      resolve: [Function: resolve],  
  7.      main:  
  8.       Module {  
  9.         id: '.',  
  10.         exports: {},  
  11.         parent: null,  
  12.         filename: '/Users/samer/index.js',  
  13.         loaded: false,  
  14.         children: [],  
  15.         paths: [Object] },  
  16.      extensions: { ... },  
  17.      cache: { '/Users/samer/index.js': [Object] } },  
  18.   '2':  
  19.    Module { 
  20.       id: '.',  
  21.      exports: {},  
  22.      parent: null,  
  23.      filename: '/Users/samer/index.js',  
  24.      loaded: false,  
  25.      children: [],  
  26.      paths: [ ... ] }, 
  27.    '3': '/Users/samer/index.js',  
  28.   '4': '/Users/samer' } 
  29.  復制代碼 

簡單了解一下這幾個參數,第一個參數exports初始時為空(未賦值),第二、三個參數require和module是和我們引用的模塊相關的實例,它們倆不是全局的。第四、五個參數__filename和__dirname分別表示了文件路徑和目錄。

整個包裝后的函數所做的事兒約等于: 

  1. function (require, module, __filename, __dirname) {  
  2.   let exports = module.exports;    
  3.   // Your Code...    
  4.   return module.exports;  
  5.  
  6. 復制代碼 

總而言之,wrapping就是將我們的模塊作用域私有化,以module.exports作為返回值將變量或方法暴露出來,以供使用。

Cache

緩存很容易理解,通過一個案例來看看吧: 

  1. echo 'console.log(`log something.`)' > index.js  
  2. // In node repl  
  3. > require('./index.js')  
  4. log something.  
  5. {}  
  6. > require('./index.js')  
  7. {}  
  8. >  
  9. 復制代碼 

可以看到,兩次引用同一個模塊,只打印了一次信息,這是因為第二次引用時取的是緩存,無需重新加載模塊。

打印require.cache可以看到當前的緩存信息: 

  1. > require.cache  
  2. [Object: null prototype] {  
  3.   '/Users/samer/index.js': Module {  
  4.     id: '/Users/samer/index.js',  
  5.     path: '/Users/samer/',  
  6.     exports: {},  
  7.     parent: Module {  
  8.       id: '<repl>',  
  9.       path: '.',  
  10.       exports: {},  
  11.       parent: undefined,  
  12.       filename: null,  
  13.       loaded: false,  
  14.       children: [Array],  
  15.       paths: [Array]  
  16.     },  
  17.     filename: '/Users/samer/index.js',  
  18.     loaded: true,  
  19.     children: [],  
  20.     paths: [  
  21.       '/Users/samer/learn-node/repl/node_modules',  
  22.       '/Users/samer/learn-node/node_modules',  
  23.       '/Users/samer/node_modules',  
  24.       '/Users/node_modules',  
  25.       '/node_modules',  
  26.       '/Users/samer/.node_modules',  
  27.       '/Users/samer/.node_libraries',  
  28.       '/usr/local/Cellar/node/7.7.1/lib/node'  
  29.     ]  
  30.   }  
  31.  
  32. 復制代碼 

可以看到剛剛引用的index.js文件處于緩存當中,因此不會重新加載模塊。當然我們也可以通過刪除require.cache來清空緩存內容,達到重新加載的目的,這里不再演示。

總結

本文概述了使用Node.js模塊化時需要了解到的一些基本原理和常識,希望幫助大家對Node.js模塊化有更清晰的認識。但更深入的細節并未在本文中闡述,例如wrapper函數內部的處理邏輯,CommonJS的同步加載的問題、與ES模塊的區別等等。這些未提到的內容大家可以在本文以外做更多探索。 

 

責任編輯:龐桂玉 來源: 前端大全
相關推薦

2011-12-09 11:16:48

Node.js

2014-07-31 17:13:50

編碼程序員

2022-11-16 08:43:30

Node.js模塊

2016-10-09 11:03:41

Javascript模塊化Web

2021-11-16 08:51:29

Node JavaScript變量類型

2016-11-01 23:16:52

光纖光纖線纜

2020-05-07 10:14:00

企業架構師CIOIT網絡

2018-01-03 11:35:34

推送AndroidiOS

2017-04-29 09:00:14

Linux程序進程

2014-04-01 13:54:32

AndroidStudioEclipse

2023-09-04 07:49:43

2025-04-29 07:40:00

勒索軟件數據泄露網絡攻擊

2015-08-11 17:55:21

谷歌重組科技

2016-02-29 09:37:44

5G

2015-09-02 10:12:17

數據安全云存儲

2012-09-19 10:37:37

jQueryJSWeb

2018-12-21 09:15:13

綜合布線屏蔽線纜

2021-04-15 08:04:27

容器DevOps程序

2023-08-16 15:57:53

2024-04-26 09:03:31

Node.jsCurrent發布版
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 中文字幕乱码视频32 | 色av一区| 日本爱爱视频 | 国产999精品久久久久久绿帽 | 一区二区在线观看av | 亚洲电影成人 | 放个毛片看看 | 欧美极品在线 | 欧美xxxx黑人又粗又长 | 欧美在线视频观看 | 精品久久精品 | 久久综合一区 | 拍戏被cao翻了h承欢 | www.国产一区 | 日韩精品在线看 | 成人a在线| 成人av播放| 爱爱爱av| 色久五月 | 毛片视频网址 | 亚洲精品视频播放 | 久久不卡视频 | 成人自拍视频网站 | www.日韩| 亚洲人成人一区二区在线观看 | 精品毛片视频 | 超碰97人人人人人蜜桃 | 欧美a视频| 午夜视频在线观看网址 | 欧美高清hd | 亚洲视频一区在线 | 成人亚洲综合 | 亚洲天堂中文字幕 | 亚洲免费精品 | 欧美日韩a | 久草免费在线视频 | 欧美日韩久久精品 | 欧美视频一区二区三区 | 高清视频一区二区三区 | 国产成人在线视频播放 | 中文字幕乱码视频32 |