歡迎您光臨本站 註冊首頁

Node.js API詳解之 module模塊用法實例分析

←手機掃碼閱讀     retouched @ 2020-06-03 , reply:0

本文實例講述了Node.js API詳解之 module模塊用法。分享給大家供大家參考,具體如下:

Node.js API詳解之 module

Node.js 有一個簡單的模塊加載系統。

在 Node.js 中,文件和模塊是一一對應的(每個文件被視為一個獨立的模塊)。

例子,假設有一個名為 foo.js 的文件:

 const circle = require('./circle.js'); console.log(`半徑為 4 的圓的面積是 ${circle.area(4)}`);


在第一行中,foo.js 加載了同一目錄下的 circle.js 模塊。

circle.js 文件的內容為:

 const { PI } = Math; exports.area = (r) => PI * r ** 2; exports.circumference = (r) => 2 * PI * r;


circle.js 模塊導出了 area() 和 circumference() 兩個函數。

通過在特殊的 exports 對象上指定額外的屬性,函數和對象可以被添加到模塊的根部。

模塊內的本地變量是私有的,因為模塊被 Node.js 包裝在一個函數中(詳見模塊包裝器)。

在這個例子中,變量 PI 是 circle.js 私有的。

module.exports 屬性可以被賦予一個新的值(例如函數或對象)。

如下,bar.js 會用到 square 模塊,square 導出一個構造函數:

 const square = require('./square.js'); const mySquare = square(2); console.log(`正方形的面積是 ${mySquare.area()}`);


square 模塊定義在 square.js 中:

 // 賦值給 `exports` 不會修改模塊,必須使用 `module.exports` module.exports = (width) => { return { area: () => width ** 2 }; };


模塊系統在 require(‘module') 模塊中實現。

模塊包裝器

說明:

在執行模塊代碼之前,Node.js 會使用一個如下的函數包裝器將其包裝:

 (function(exports, require, module, __filename, __dirname) { // 模塊的代碼實際上在這裡 });


通過這樣做,Node.js 實現了以下幾點:
它保持了頂層的變量(用 var、const 或 let 定義)作用在模塊範圍內,而不是全局對象。
它有助於提供一些看似全局的但實際上是模塊特定的變量,例如:
實現者可以用於從模塊中導出值的 module 和 exports 對象。
包含模塊絕對文件名和目錄路徑的快捷變量 __filename 和 __dirname 。

文件模塊

說明:

如果按確切的文件名沒有找到模塊,則 Node.js 會嘗試帶上 .js、.json 或 .node 拓展名再加載。
.js 文件會被解析為 Javascript 文本文件,
.json 文件會被解析為 JSON 文本文件。
.node 文件會被解析為通過 dlopen 加載的編譯後的插件模塊。
以 ‘/' 為前綴的模塊是文件的絕對路徑。 例如,require(‘/home/marco/foo.js') 會加載 /home/marco/foo.js 文件。
以 ‘./' 為前綴的模塊是相對於調用 require() 的文件的。 也就是說,circle.js 必須和 foo.js 在同一目錄下以便於 require(‘./circle') 找到它。
當沒有以 ‘/'、'./' 或 ‘../' 開頭來表示文件時,這個模塊必須是一個核心模塊或加載自 node_modules 目錄。
如果給定的路徑不存在,則 require() 會拋出一個 code 屬性為 ‘MODULE_NOT_FOUND' 的 Error。

目錄作為模塊

說明:

可以把程序和庫放到一個單獨的目錄,然後提供一個單一的入口來指向它。
把目錄遞給 require() 作為一個參數,有三種方式。
第一種方式是在根目錄下創建一個 package.json 文件,並指定一個 main 模塊。

例子,package.json 文件類似:

 { "name" : "some-library", "main" : "./lib/some-library.js" }


如果這是在 ./some-library 目錄中,則 require(‘./some-library') 會試圖加載 ./some-library/lib/some-library.js。
這就是 Node.js 處理 package.json 文件的方式。
注意:如果 package.json 中 “main” 入口指定的文件不存在,則無法解析,Node.js 會將模塊視為不存在,並拋出默認錯誤:

Error: Cannot find module 'some-library'

如果目錄裡沒有 package.json 文件,則 Node.js 就會試圖加載目錄下的 index.js 或 index.node 文件。
例如,如果上面的例子中沒有 package.json 文件,則 require(‘./some-library') 會試圖加載:
./some-library/index.js
./some-library/index.node

模塊加載順序

說明:

如果傳遞給 require() 的模塊標識符不是一個核心模塊,也沒有以 ‘/' 、 ‘../' 或 ‘./' 開頭,
則 Node.js 會從當前模塊的父目錄開始,嘗試從它的 /node_modules 目錄里加載模塊。
Node.js 不會附加 node_modules 到一個已經以 node_modules 結尾的路徑上。
如果還是沒有找到,則移動到再上一層父目錄,直到文件系統的根目錄。
例子,如果在 ‘/home/ry/projects/foo.js' 文件裡調用了 require(‘bar.js'),則 Node.js 會按以下順序查找:
/home/ry/projects/node_modules/bar.js
/home/ry/node_modules/bar.js
/home/node_modules/bar.js
/node_modules/bar.js
通過在模塊名後包含一個路徑後綴,可以請求特定的文件或分佈式的子模塊。
例如,require(‘example-module/path/to/file') 會把 path/to/file 解析成相對於 example-module 的位置。
後綴路徑同樣遵循模塊的解析語法。

從全局目錄加載

說明:

如果 NODE_PATH 環境變量被設為一個以冒號分割的絕對路徑列表,則當在其他地方找不到模塊時 Node.js 會搜索這些路徑。
注意:在 Windows 系統中,NODE_PATH 是以分號間隔的。
在當前的模塊解析算法運行之前,NODE_PATH 最初是創建來支持從不同路徑加載模塊的。
雖然 NODE_PATH 仍然被支持,但現在不太需要,因為 Node.js 生態系統已制定了一套存放依賴模塊的約定。
有時當人們沒意識到 NODE_PATH 必須被設置時,依賴 NODE_PATH 的部署會出現意料之外的行為。
有時一個模塊的依賴會改變,導致在搜索 NODE_PATH 時加載了不同的版本(甚至不同的模塊)。
此外,Node.js 還會搜索以下位置:
1: $HOME/.node_modules
2: $HOME/.node_libraries
3: $PREFIX/lib/node
其中 $HOME 是用戶的主目錄,$PREFIX 是 Node.js 裡配置的 node_prefix。
這些主要是歷史原因。
注意:強烈建議將所有的依賴放在本地的 node_modules 目錄。 這樣將會更快地加載,且更可靠。

緩存

說明:

模塊在第一次加載後會被緩存。 這也意味著(類似其他緩存機制)如果每次調用 require(‘foo') 都解析到同一文件,則返回相同的對象。
多次調用 require(foo) 不會導致模塊的代碼被執行多次。 這是一個重要的特性。
藉助它, 可以返回“部分完成”的對象,從而允許加載依賴的依賴, 即使它們會導致循環依賴。
如果想要多次執行一個模塊,可以導出一個函數,然後調用該函數。
注意:
模塊是基於其解析的文件名進行緩存的。 由於調用模塊的位置的不同,模塊可能被解析成不同的文件名(比如從 node_modules 目錄加載),
這樣就不能保證 require(‘foo') 總能返回完全相同的對象。
此外,在不區分大小寫的文件系統或操作系統中,被解析成不同的文件名可以指向同一文件,但緩存仍然會將它們視為不同的模塊,並多次重新加載。
例如,require(‘./foo') 和 require(‘./FOO') 返回兩個不同的對象,而不會管 ./foo 和 ./FOO 是否是相同的文件。

循環

說明:

當循環調用 require() 時,一個模塊可能在未完成執行時被返回。

例如以下情況:
a.js:

 console.log('a 開始'); exports.dOne= false; const b = require('./b.js'); console.log('在 a 中,b.dOne= %j', b.done); exports.dOne= true; console.log('a 結束');


b.js:

 console.log('b 開始'); exports.dOne= false; const a = require('./a.js'); console.log('在 b 中,a.dOne= %j', a.done); exports.dOne= true; console.log('b 結束');


main.js:

 console.log('main 開始'); const a = require('./a.js'); const b = require('./b.js'); console.log('在 main 中,a.dOne=%j,b.dOne=%j', a.done, b.done);


當 main.js 加載 a.js 時,a.js 又加載 b.js。
此時,b.js 會嘗試去加載 a.js。
為了防止無限的循環,會返回一個 a.js 的 exports 對象的 未完成的副本 給 b.js 模塊。
然後 b.js 完成加載,並將 exports 對象提供給 a.js 模塊。
當 main.js 加載這兩個模塊時,它們都已經完成加載。 因此,該程序的輸出會是:

$ node main.js
main 開始
a 開始
b 開始
在 b 中,a.dOne= false
b 結束
在 a 中,b.dOne= true
a 結束
在 main 中,a.dOne=true,b.dOne=true

__dirname

說明:

當前模塊的文件夾名稱。等同於 __filename 的 path.dirname() 的值。

demo:

 console.log(__dirname); // /Users/xiaoqiang/Documents/work/demo/NodeApi


__filename

說明:

當前模塊的文件名稱―解析後的絕對路徑。
在主程序中這不一定要跟命令行中使用的名稱一致。

demo:

 console.log(__filename); // /Users/xiaoqiang/Documents/work/demo/NodeApi/app.js


module.exports

說明:

module.exports 對象是由模塊系統創建的。
有時這是難以接受的;許多人希望他們的模塊成為某個類的實例。
為了實現這個,需要將期望導出的對象賦值給 module.exports。
注意,將期望的對象賦值給 exports 會簡單地重新綁定本地 exports 變量,這可能不是期望的。
注意,對 module.exports 的賦值必須立即完成。 不能在任何回調中完成。

demo:

 // a.js const EventEmitter = require('events'); module.exports = new EventEmitter(); // app.js const a = require('./a'); a.on('ready', () => { console.log('模塊 a 已準備好'); });


exports

說明:

這是一個對於 module.exports 的更簡短的引用形式。
exports 變量是在模塊的文件級別作用域內有效的,它在模塊被執行前被賦予 module.exports 的值。
它有一個快捷方式,以便 module.exports.f = …
可以被更簡潔地寫成 exports.f = …。
注意,就像任何變量,如果一個新的值被賦值給 exports,它就不再綁定到 module.exports:

demo:

 // a.js const EventEmitter = require('events'); exports.events = new EventEmitter(); // app.js const {events} = require('./a'); events.on('ready', () => { console.log('模塊 a 已準備好'); });


require()

說明:

使用該方法引入模塊。

demo:

 const {events} = require('./a'); events.on('ready', () => { console.log('模塊 a 已準備好'); });


require.main

說明:

當 Node.js 直接運行一個文件時,require.main 會被設為它的 module。
這意味著可以通過 require.main === module 來判斷一個文件是否被直接運行:
對於 foo.js 文件,如果通過 node foo.js 運行則為 true,但如果通過 require(‘./foo') 運行則為 false。
因為 module 提供了一個 filename 屬性(通常等同於 __filename),
所以可以通過檢查 require.main.filename 來獲取當前應用程序的入口點。

demo:

 const events = require('./a'); console.log(require.main); // Module { // id: '.', // exports: {}, // parent: null, // filename: '/Users/xiaoqiang/Documents/work/demo/NodeApi/app.js', // loaded: false, // children: // [ Module { // id: '/Users/xiaoqiang/Documents/work/demo/NodeApi/a.js', // exports: [Object], // parent: [Circular], // filename: '/Users/xiaoqiang/Documents/work/demo/NodeApi/a.js', // loaded: true, // children: [], // paths: [Array] } ], // paths: // [ '/Users/xiaoqiang/Documents/work/demo/NodeApi/node_modules', // '/Users/xiaoqiang/Documents/work/demo/node_modules', // '/Users/xiaoqiang/Documents/work/node_modules', // '/Users/xiaoqiang/Documents/node_modules', // '/Users/xiaoqiang/node_modules', // '/Users/node_modules', // '/node_modules' ] // }


require.cache

說明:

被引入的模塊將被緩存在這個對象中。
從此對象中刪除鍵值對將會導致下一次 require 重新加載被刪除的模塊。
注意不能刪除 native addons(原生插件),因為它們的重載將會導致錯誤。

demo:

 const events = require('./a'); console.log(require.cache); // { '/Users/xiaoqiang/Documents/work/demo/NodeApi/app.js': // Module { // id: '.', // exports: {}, // parent: null, // filename: '/Users/xiaoqiang/Documents/work/demo/NodeApi/app.js', // loaded: false, // children: [ [Module] ], // paths: // [ '/Users/xiaoqiang/Documents/work/demo/NodeApi/node_modules', // '/Users/xiaoqiang/Documents/work/demo/node_modules', // '/Users/xiaoqiang/Documents/work/node_modules', // '/Users/xiaoqiang/Documents/node_modules', // '/Users/xiaoqiang/node_modules', // '/Users/node_modules', // '/node_modules' ] }, // '/Users/xiaoqiang/Documents/work/demo/NodeApi/a.js': // Module { // id: '/Users/xiaoqiang/Documents/work/demo/NodeApi/a.js', // exports: { events: [EventEmitter] }, // parent: // Module { // id: '.', // exports: {}, // parent: null, // filename: '/Users/xiaoqiang/Documents/work/demo/NodeApi/app.js', // loaded: false, // children: [Array], // paths: [Array] }, // filename: '/Users/xiaoqiang/Documents/work/demo/NodeApi/a.js', // loaded: true, // children: [], // paths: // [ '/Users/xiaoqiang/Documents/work/demo/NodeApi/node_modules', // '/Users/xiaoqiang/Documents/work/demo/node_modules', // '/Users/xiaoqiang/Documents/work/node_modules', // '/Users/xiaoqiang/Documents/node_modules', // '/Users/xiaoqiang/node_modules', // '/Users/node_modules', // '/node_modules' ] // } // }


require.extensions (已廢棄)

說明:

指示 require 怎樣處理特定的文件擴展名。
以前這被用來將非 Javascript 模塊按需編譯後加載到 Node.js 中。
然而,在實踐中,有更多更好的解決方案,比如用其它 Node.js 程序加載模塊, 或者提前將它們編譯為 Javascript 模塊。
由於模塊系統已鎖定,這個特性可能永遠不會消失,但是鑑於其複雜性和可能導致的小問題, 最好不要碰它。
例如:把 .sjs 文件當做 .js 文件處理:

demo:

 require.extensions['.sjs'] = require.extensions['.js'];


require.resolve(request[, options])

說明:

使用內部的 require() 機制查詢模塊的位置, 此操作只返回解析後的文件名,不會加載該模塊。
request:需要解析的模塊路徑。
options.paths:解析模塊的起點路徑數組。此參數存在時,將使用這些路徑而非默認解析路徑。

demo:

 const events = require('./a'); console.log( require.resolve('./a') ); // /Users/xiaoqiang/Documents/work/demo/NodeApi/a.js


require.resolve.paths(request)

說明:

返回一個數組,其中包含解析 request 過程中被查詢的路徑。
request:被查詢解析路徑的模塊的路徑。

demo:

 const events = require('./a'); console.log( require.resolve.paths('./a') ); // [ '/Users/xiaoqiang/Documents/work/demo/NodeApi' ]


module

說明:

返回對當前模塊的引用,是一個module對象。

demo:

 const events = require('./a'); console.log( module ); // Module { // id: '.', // exports: {}, // parent: null, // filename: '/Users/xiaoqiang/Documents/work/demo/NodeApi/app.js', // loaded: false, // children: // [ Module { // id: '/Users/xiaoqiang/Documents/work/demo/NodeApi/a.js', // exports: [Object], // parent: [Circular], // filename: '/Users/xiaoqiang/Documents/work/demo/NodeApi/a.js', // loaded: true, // children: [], // paths: [Array] } ], // paths: // [ '/Users/xiaoqiang/Documents/work/demo/NodeApi/node_modules', // '/Users/xiaoqiang/Documents/work/demo/node_modules', // '/Users/xiaoqiang/Documents/work/node_modules', // '/Users/xiaoqiang/Documents/node_modules', // '/Users/xiaoqiang/node_modules', // '/Users/node_modules', // '/node_modules' ] // }


module 對象

說明:

在每個模塊中,module 的自由變量是一個指向表示當前模塊的對象的引用。
為了方便,module.exports 也可以通過全局模塊的 exports 對象訪問。
module 實際上不是全局的,而是每個模塊本地的。

module.id

說明:

返回模塊的標識符。 通常是完全解析後的文件名。

demo:

 const events = require('events'); console.log( module.id ); // .


module.parent

說明:

最先引用該模塊的模塊。

demo:

 const events= require('events'); console.log( module.parent ); // null


module.children

說明:

返回被該模塊引用的模塊對象。

demo:

 const events = require('events'); console.log( module.children ); // []


module.filename

說明:

返回模塊的完全解析後的文件名。

demo:

 const events = require('events'); console.log( module.filename ); // /Users/xiaoqiang/Documents/work/demo/NodeApi/app.js


module.loaded

說明:

返回模塊是否已經加載完成,或正在加載中。

demo:

 const events = require('events'); console.log( module.loaded ); // false


module.paths

說明:

返回模塊的搜索路徑。

demo:

 const events = require('events'); console.log( module.paths ); // [ '/Users/xiaoqiang/Documents/work/demo/NodeApi/node_modules', // '/Users/xiaoqiang/Documents/work/demo/node_modules', // '/Users/xiaoqiang/Documents/work/node_modules', // '/Users/xiaoqiang/Documents/node_modules', // '/Users/xiaoqiang/node_modules', // '/Users/node_modules', // '/node_modules' ]


希望本文所述對大家node.js程序設計有所幫助。


[retouched ] Node.js API詳解之 module模塊用法實例分析已經有238次圍觀

http://coctec.com/docs/javascript/show-post-236764.html