一起讀nodejs(三)----模組(Modules)

來源:互聯網
上載者:User

模組(Modules)

    stability:5 -locked

    node有一個簡單的模組載入機制.在node裡面檔案和模組是 一對一 對應的.例如,foo.js載入在同一檔案夾下的circle.js模組.

    foo.js的內容:

[javascript] 
<SPAN style="FONT-SIZE: 18px; FONT-FAMILY: FangSong_GB2312">var circle = require('./circle.js'); 
console.log( 'The area of a circle of radius 4 is ' 
           + circle.area(4));</SPAN> 

var circle = require('./circle.js');
console.log( 'The area of a circle of radius 4 is '
           + circle.area(4));    circle.js的內容:[javascript] view plaincopyprint?
<SPAN style="FONT-SIZE: 18px; FONT-FAMILY: FangSong_GB2312">var PI = Math.PI; 
 
exports.area = function (r) { 
  return PI * r * r; 
}; 
 
exports.circumference = function (r) { 
  return 2 * PI * r; 
};</SPAN> 

var PI = Math.PI;

exports.area = function (r) {
  return PI * r * r;
};

exports.circumference = function (r) {
  return 2 * PI * r;
};    circle.js模組以匯出了area()方法和circumference()方法.為了匯出一個對象,需要加上指定的exports對象.

    屬於模組的變數是私人的.在這個例子中變數PI就是私人於circle.js.

    模組機制是在require('module')模組中實現的.

 


      環形載入(Cycles)

 


    當存在環形的require()調用時,當一個模組被返回被執行的時候,可能還沒有載入完成,考慮下面這種情景:
    a.js:
[javascript] 
<SPAN style="FONT-SIZE: 18px; FONT-FAMILY: FangSong_GB2312">console.log('a starting'); 
exports.done = false; 
var b = require('./b.js'); 
console.log('in a, b.done = %j', b.done); 
exports.done = true; 
console.log('a done');</SPAN> 

console.log('a starting');
exports.done = false;
var b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');    b.js:[javascript] view plaincopyprint?
<SPAN style="FONT-SIZE: 18px; FONT-FAMILY: FangSong_GB2312">console.log('b starting'); 
exports.done = false; 
var a = require('./a.js'); 
console.log('in b, a.done = %j', a.done); 
exports.done = true; 
console.log('b done');</SPAN> 

console.log('b starting');
exports.done = false;
var a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');    main.js:[javascript] view plaincopyprint?
<SPAN style="FONT-SIZE: 18px; FONT-FAMILY: FangSong_GB2312">console.log('main starting'); 
var a = require('./a.js'); 
var b = require('./b.js'); 
console.log('in main, a.done=%j, b.done=%j', a.done, b.done);</SPAN> 

console.log('main starting');
var a = require('./a.js');
var b = require('./b.js');
console.log('in main, a.done=%j, b.done=%j', a.done, b.done);    當main.js載入a.js的時候,a.js依次需要載入b.js.在這時,b.js試著去載入a.js.為了防止一個無窮的迴圈(loop),一個未完成的a.js的副本的exports匯出對象被返回到b.js模組.然後b.js完成載入,然後他的exports匯出對象被提供給a.js模組.


    當main.js載入完這兩個模組的時候,他們都已經載入完.這個程式的輸出應該這樣子:[javascript] view plaincopyprint?
<SPAN style="FONT-SIZE: 18px; FONT-FAMILY: FangSong_GB2312">$ node main.js 
main starting 
a starting 
b starting 
in b, a.done = false 
b done 
in a, b.done = true 
a done 
in main, a.done=true, b.done=true</SPAN> 

$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done=true, b.done=true    如果你的程式裡面有環形的模組依賴,確保制定相應的計劃(意思是,確保在所有依賴的模組都載入完時在調用,避免在模組未載入完時,調用導致程式不可預期的結果).


(迴圈載入,require()方法有對應的機制,上面例子中b.js返回時,b.js載入的a.js沒有載入完,但是當b.js載入完時,a.js就載入完了,這時候b.js裡面儲存的a.js也會被更新成完成的對象,其實我覺得儲存的可能就是類似於a.js的引用.)
      核心模組(Core modules)


    node中有一些模組被編譯成二進位. 這些模組在文檔的其他地方有更詳細的描述.


    核心模組被定義在node的源碼中的lib檔案夾下.


    如果核心模組的標識符被傳入require()方法時,總是被優先載入.例如,require('http')將總是返回內建的http模組,即使當前有一個同名的模組檔案夾.


      檔案模組載入機制(File modules)


    如果準確的檔案名稱字沒有找到,node將會試圖依次載入添加了.js,.json,.node等尾碼名的檔案.
    .js檔案被當作javascript文字檔解讀,.json檔案被轉換成json文字檔. .node檔案被當做已經編譯好的外掛程式模組,使用dloopen載入.(在dlopen()函數以指定模式開啟指定的動態串連庫檔案,並返回一個控制代碼給調用進程。使用dlclose()來卸載開啟的庫。)


    當一個模組的首碼是"/"時,表示一個模組檔案的絕對路徑.例如:require('/home/marco/foo.js')將會使用'/home/marco/foo.js'作為路徑載入.


    當一個模組的首碼是"./"時,表示一個模組檔案的相對路徑調用require().例如:為了讓foo.js中的require('./circle.js')能被找到,circle.js必須和foo.js在同一目錄下.


    如果沒有"./"或者"/"當檔案首碼,這個模組不是一個核心模組,就是一個需要從node_modules檔案夾中載入的模組.


    如果給定的載入路徑不存在,requier()方法將會拋出一個Error錯誤對象,並且Error的code屬性被設定成:'MODULE_NOT_FOUND'.   
    
      從node_modules檔案夾載入模組(loading from node_mudoles folders)


    如果傳入到require()方法中的模組標識符不是本地模組,並且也不是"/","../",或者"./"開頭,node則會在當前模組的父目錄路徑上追加/node_modules,以這個路徑試圖載入模組.


    如果這裡也沒有找到,node會繼續移動到更上一層的父目錄,等等...直到到達根目錄.


    例如,檔案'/home/ry/project/foo.js'中調用require('bar.js'),node會尋找以下列出的路徑,依次是:
/home/ry/projects/node_modules/bar.js
/home/ry/node_modules/bar.js
/home/node_modules/bar.js
/node_modules/bar.js
    這將允許程式本地化他們的依賴,以便不產生衝突.


      檔案夾就是模組(Floder as modules)


    組織程式和類庫放進自己包含的目錄下是很方便的,然後提供一個單獨的入口指定這個類庫,有三種方法把一個檔案夾作為一個參數傳進require()方法.


    第一種方法是在根目錄建立一個package.json檔案,指定一個主模組.一個paceage.json的例子看起來像這樣子:

[javascript] 
<SPAN style="FONT-SIZE: 18px; FONT-FAMILY: FangSong_GB2312">{ "name" : "some-library", 
  "main" : "./lib/some-library.js" }</SPAN> 

{ "name" : "some-library",
  "main" : "./lib/some-library.js" }    如果當前程式路徑是在./some-library目錄下,則require('./some-library')將會試圖載入路徑./some-library/lib/some-library.js.


    這是node自動拓展自package.json檔案的結果.


    如果這個目錄下沒有出現package.json檔案,node將會試圖在這個目錄載入一個index.js或者index.node檔案出來.例如,如果在上面的例子中沒有package.json檔案,則require('./sone-library')將會試圖載入:[javascript] view plaincopyprint?
<SPAN style="FONT-SIZE: 18px; FONT-FAMILY: FangSong_GB2312">./some-library/index.js 
./some-library/index.node</SPAN> 

./some-library/index.js
./some-library/index.node      緩衝(Caching)

    當模組被第一次載入之後就被緩衝了起來,這意味著在其他地方調用require('foo')將會獲得一樣一樣的對象被返回,如果他們掃描的是同一檔案的情況下.


    多次調用require('foo')不會引起模組代碼被執行多次.這是一個重要的特性.有了它,部分載入完成的對象可以被返回,進而允許載入過渡期的依賴對象,甚至當他們引起環形載入.
    (上面這段話,其實就是對本文剛開始介紹的環形載入的一種說明,就是說,在node中require()方法載入對象時,可能還沒載入完就返回了,但是返回的是一個索引而已,在一個node應用中,使用require()載入一個模組,都會先在緩衝中尋找,有就返回,沒有在載入.)

    如果你想要一個模組的代碼被執行多次,你可以匯出一個方法,然後調用這個方法.

    緩衝模組提醒(Module Caching caveats)

    模組是以他們被解析出的檔案名稱為基礎緩衝起來的,因為模組可能由於調用require()方法的當前路徑不一樣,而導致解析出不一樣的檔案名稱,(從node_modules檔案夾載入),如果解析出不同的檔案,將不能保證require('foo')總是會返回一致的對象.
    module對象(The module Object)

    在每一個模組中,自由變數module是一個代表當前模組的引用.因此module.exports和exports對象是一樣的.module實際上不是一個全域變數,但是內建於每一個模組中.
    module.exports

    exports對象是被模組機制建立的,有時候,許多情況需要讓模組成為一些類的一個執行個體,而使exports不可訪問,為了把渴望匯出的對象分配給moduel.exports對象,在例子中假設我們需要編寫一個a.js模組.[javascript] view plaincopyprint?
<SPAN style="FONT-SIZE: 18px; FONT-FAMILY: FangSong_GB2312">var EventEmitter = require('events').EventEmitter; 
 
module.exports = new EventEmitter(); 
 
// Do some work, and after some time emit  
// the 'ready' event from the module itself.  
setTimeout(function() { 
  module.exports.emit('ready'); 
}, 1000);</SPAN> 

var EventEmitter = require('events').EventEmitter;

module.exports = new EventEmitter();

// Do some work, and after some time emit
// the 'ready' event from the module itself.
setTimeout(function() {
  module.exports.emit('ready');
}, 1000);在另一個檔案裡我們需要做:[javascript] view plaincopyprint?
<SPAN style="FONT-SIZE: 18px; FONT-FAMILY: FangSong_GB2312">var a = require('./a'); 
a.on('ready', function() { 
  console.log('module a is ready'); 
});</SPAN> 

var a = require('./a');
a.on('ready', function() {
  console.log('module a is ready');
});注意,這種指派至到module.exports對象的操作一定要立刻執行,如果放在任何一個回呼函數裡面,將不會生效.


x.js[javascript] view plaincopyprint?
<SPAN style="FONT-SIZE: 18px; FONT-FAMILY: FangSong_GB2312">setTimeout(function() { 
  module.exports = { a: "hello" }; 
}, 0);</SPAN> 

setTimeout(function() {
  module.exports = { a: "hello" };
}, 0);y.js[javascript] view plaincopyprint?
<SPAN style="FONT-SIZE: 18px; FONT-FAMILY: FangSong_GB2312">var x = require('./x'); 
console.log(x.a);</SPAN> 

var x = require('./x');
console.log(x.a);

轉載請註明出處:http://blog.csdn.net/qq413041153/article/details/7922126
    module.require(id)
id String
return:從解析出的模組中匯出的對象.
    module.require()方法提供了一種方式去載入一個在之前已經載入過的模組.
注意,為了實現這種方式,你必須獲得一個這個模組的索引,自從require()方法返回了exports對象之後,這個模組只是在這個一段指定的代碼裡有效,為了使用你需要要明確的匯出他.
    module.id
String
    模組的標識符,是一個能代表模組的完整路徑
    module.filename
String
    一個能代表模組的完整路徑
    module.loaded
boolean
    代表這個模組是載入完成還是正在載入中.
    module.parent
module object
    代表載入當前模組的模組.
    module.children
Array
    代表當前模組載入的子模組.
   總結(All together...)
    為了在調用require()時獲得準確的檔案名稱被載入,使用require.resolve()方法.
    上面的代碼放在一起,下面是require.resolve()方法的進階演算法的虛擬碼:[javascript] view plaincopyprint?
<SPAN style="FONT-SIZE: 18px; FONT-FAMILY: FangSong_GB2312">require(X) from module at path Y 
1. If X is a core module, 
   a. return the core module 
   b. STOP 
2. If X begins with './' or '/' or '../' 
   a. LOAD_AS_FILE(Y + X) 
   b. LOAD_AS_DIRECTORY(Y + X) 
3. LOAD_NODE_MODULES(X, dirname(Y)) 
4. THROW "not found" 
 
LOAD_AS_FILE(X) 
1. If X is a file, load X as JavaScript text.  STOP 
2. If X.js is a file, load X.js as JavaScript text.  STOP 
3. If X.node is a file, load X.node as binary addon.  STOP 
 
LOAD_AS_DIRECTORY(X) 
1. If X/package.json is a file, 
   a. Parse X/package.json, and look for "main" field. 
   b. let M = X + (json main field) 
   c. LOAD_AS_FILE(M) 
2. If X/index.js is a file, load X/index.js as JavaScript text.  STOP 
3. If X/index.node is a file, load X/index.node as binary addon.  STOP 
 
LOAD_NODE_MODULES(X, START) 
1. let DIRS=NODE_MODULES_PATHS(START) 
2. for each DIR in DIRS: 
   a. LOAD_AS_FILE(DIR/X) 
   b. LOAD_AS_DIRECTORY(DIR/X) 
 
NODE_MODULES_PATHS(START) 
1. let PARTS = path split(START) 
2. let ROOT = index of first instance of "node_modules" in PARTS, or 0 
3. let I = count of PARTS - 1 
4. let DIRS = [] 
5. while I > ROOT, 
   a. if PARTS[I] = "node_modules" CONTINUE 
   c. DIR = path join(PARTS[0 .. I] + "node_modules") 
   b. DIRS = DIRS + DIR 
   c. let I = I - 1 
6. return DIRS</SPAN> 

require(X) from module at path Y
1. If X is a core module,
   a. return the core module
   b. STOP
2. If X begins with './' or '/' or '../'
   a. LOAD_AS_FILE(Y + X)
   b. LOAD_AS_DIRECTORY(Y + X)
3. LOAD_NODE_MODULES(X, dirname(Y))
4. THROW "not found"

LOAD_AS_FILE(X)
1. If X is a file, load X as JavaScript text.  STOP
2. If X.js is a file, load X.js as JavaScript text.  STOP
3. If X.node is a file, load X.node as binary addon.  STOP

LOAD_AS_DIRECTORY(X)
1. If X/package.json is a file,
   a. Parse X/package.json, and look for "main" field.
   b. let M = X + (json main field)
   c. LOAD_AS_FILE(M)
2. If X/index.js is a file, load X/index.js as JavaScript text.  STOP
3. If X/index.node is a file, load X/index.node as binary addon.  STOP

LOAD_NODE_MODULES(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
   a. LOAD_AS_FILE(DIR/X)
   b. LOAD_AS_DIRECTORY(DIR/X)

NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let ROOT = index of first instance of "node_modules" in PARTS, or 0
3. let I = count of PARTS - 1
4. let DIRS = []
5. while I > ROOT,
   a. if PARTS[I] = "node_modules" CONTINUE
   c. DIR = path join(PARTS[0 .. I] + "node_modules")
   b. DIRS = DIRS + DIR
   c. let I = I - 1
6. return DIRS   從通用檔案夾載入模組(loading from global folders)

    如果NODE_PATH環境變數被設定成一個以逗號分割的絕對路徑列表,node如果沒有在其他地方找到模組將會搜尋這些路徑,(注意:在windows中NODE_PATH是以分好為分隔字元.)
    另外,node將會搜尋一下路徑:[javascript] view plaincopyprint?
<SPAN style="FONT-SIZE: 18px; FONT-FAMILY: FangSong_GB2312">1: $HOME/.node_modules 
2: $HOME/.node_libraries 
3: $PREFIX/lib/node</SPAN> 

1: $HOME/.node_modules
2: $HOME/.node_libraries
3: $PREFIX/lib/node$HOME是使用者的home目錄,$PREFIX是node配置的node_prefix.


這些基本上都是一些曆史遺留問題.建議你把你的依賴放進node_modules檔案夾中,這樣的話,載入更快,更可靠.


    訪問主模組(Accessing the main module)


    當一個檔案是直接從node執行的,那麼require.main變數會設定成module對象,這意味著你可以在測試時判斷一個模組是否是直接啟動並執行.[javascript] view plaincopyprint?
<SPAN style="FONT-SIZE: 18px; FONT-FAMILY: FangSong_GB2312">require.main === module</SPAN> 

require.main === module
    例如foo.js,如果通過node foo.js運行,將會返回true,如果通過require('./foo'),將會返回false.
    因為module提供了一個filename對象,(正常情況下和_filename等價),當前程式的進入點可以通過require.main.filename獲得.
    附錄:包管理建議(Package Manager Tips)

    理論上,require()方法被設計成一般情況下,可以滿足載入若干合理的檔案結構目錄.包管理程式例如:dbkg,rpm,and npm,在沒有修改的情況下,是可以從node Modules中載入,建立本地packages.
    下面我們給出了一些建議的目錄結構:


    比方說,我們想在目錄:/user/lib/node/<some-package>/<some-version>下儲存一個包的指定版本,


    packages可以依賴另一個,為了安裝包foo,你可能不得不安裝bar包的一個指定版本.而且bar包可能還有自己的一些依賴,在某種情況下,這些依賴可能有衝突或者形成環形載入.
    自從node尋找它載入的每一個模組的真是路徑,然後就像上面描述的那樣,在node_modules中尋找他們的依賴,用下面的這種結構,這種情況很簡單就可以解決:[javascript] view plaincopyprint?
<SPAN style="FONT-SIZE: 18px; FONT-FAMILY: FangSong_GB2312">/usr/lib/node/foo/1.2.3/ - Contents of the foo package, version 1.2.3. 
/usr/lib/node/bar/4.3.2/ - Contents of the bar package that foo depends on. 
/usr/lib/node/foo/1.2.3/node_modules/bar - Symbolic link to /usr/lib/node/bar/4.3.2/. 
/usr/lib/node/bar/4.3.2/node_modules/* - Symbolic links to the packages that bar depends on.</SPAN> 

/usr/lib/node/foo/1.2.3/ - Contents of the foo package, version 1.2.3.
/usr/lib/node/bar/4.3.2/ - Contents of the bar package that foo depends on.
/usr/lib/node/foo/1.2.3/node_modules/bar - Symbolic link to /usr/lib/node/bar/4.3.2/.
/usr/lib/node/bar/4.3.2/node_modules/* - Symbolic links to the packages that bar depends on.
    因此,即使發生環形載入,或者依賴衝突,每個模組都能夠獲得一個他們能夠使用的版本.
    擋在foo包中執行require('bar'),將會通過解析出來的路徑串連字元/user/lib/node/foo/1.2.3/node_modules/bar獲得指定版本的bar.然後,當bar包中執行require('quux')時,將會通過解析出來的路徑串連字元/user/lib/node/bar/4.3.2/node_modules/quux獲得指定版本的quux.
    此外,為了讓模組的尋找過程更理想,不要直接把包放在/usr/lib/node下,我們可以把他們放在/usr/lib/node_modules/<name>/<version>下.這樣node就可以不用麻煩的在/usr/node_modules/或者/node_modules下尋找找不到的包.
    為了確保模組在repl中是可見的,將/usr/lib/node_modules添加到$NODE_PATH環境變數中可能會起到效果.因為模組查到用到的其他模組全部是相對的,並且require()的 載入也是基於真實路徑的,所以,包可以放在任何地方.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.