Node解析之----模塊機制篇

開篇前,咱們先來看張圖, 看node與W3C組織、CommonJS組織、ECMAScript之間的關係。javascript

 

 

    Node借鑑來CommonJS的Modules規範實現了一套很是易用的模塊系統,NPM對Packages規範
的無缺支持使得Node應用在開發過程當中事半功倍。html

1、CommonJS 的模塊規範html5

CommonJS中的大部分規範涵蓋了模塊、二進制、Buffer、字符集編碼、I/O流、進程環境、文件系統、套接字、單元測試、Web服務器網關接口、包管理等。java

 

1.1 模塊引用
模塊示例代碼以下:node

var math = require('math'); 

在CommonJS規範中,存在require()方法,這個方法接受模塊標識,以此引入一個模塊的API到當前上下文中。web

1.2模塊定義express

在模塊中,上下文提供require()方法引入外部模塊。對應引入的功能,上下文提供了exports對象用於導出當前模塊的方法或者變量,而且它是惟一導出的出口。npm

例如:json

// math.js
exports.add = function () { 
 var sum = 0, 
 i = 0, 
 args = arguments, 
 l = args.length; 
 while (i < l) { 
sum += args[i++]; 
 } 
 return sum; 
}; 

// program.js
var math = require('math'); 
exports.increment = function (val) { 
 return math.add(val, 1); 
}; 

1.3模塊標識api

    簡單理解就是傳遞給require()方法的參數,必須使用小駝峯命名的字符串,或者使用相對路徑,亦可以使用絕對路徑。

Tip: 能夠沒有.js文件名後綴。

 

CommonJS導出和引入機制,咱們不須要考慮變量污染、命名空間等。

 

2、Node的模塊實現

在Node中引入模塊,要經歷3個步驟:
(1) 路徑引入
(2) 文件定位
(3) 編譯執行

Node模塊分兩種,一種是Node提供的模塊,稱爲核心模塊;另一種是用戶編寫的模塊,稱爲文件模塊。

 一、核心模塊在Node源代碼的編譯過程當中,編譯進了二進制執行文件。在Node進程啓動時,部分核心模塊就被直接加載進內存中,因此這部分核心模塊引入時,文件定位和編譯執行這兩個步驟能夠省略掉,而且在路徑分析中優先判斷,因此它的加載速度是最快的。

二、文件模塊在運行時動態加載,須要完整的路徑分析、文件定位、編譯執行過程,速度比核心模塊慢。

2.1優先從緩存加載

2.2路徑分析和文件定位

Tip: 同步配合緩存,緩解Node單線程中阻塞式調用的缺陷

1)模塊標識符分析

    require()方法接受一個標識符做爲參數,在Node實現中,正是基於這樣一個標識符進行模塊查找的。

2)自定義模塊

    它的生成方式與Javascript的原型鏈的查找方式十分類型。在加載過程當中,Node會逐個嘗試模塊路徑中的路徑,知道找到目標文件爲止。能夠看出,當前文件的路徑越深,模塊查找耗時會越多,這是自定義模塊加載速遞最慢的緣由。

2.3模塊編譯

每一個文件模塊都是一個對象,例如:

function Module(id, parent) { this.id = id;
this.exports = {}; this.parent = parent;
       if (parent && parent.children) {
         parent.children.push(this);
}
this.filename = null; this.loaded = false; this.children = [];
}

編譯和執行是引入文件模塊的最後一個階段。定位到具體的文件後,Node會新建一個模塊對象,而後根據路徑載入並編譯。對於不一樣的文件拓展名,其載入方法也有所不一樣,例如:

1).js文件:經過fs模塊同步讀取文件後編譯執行

2).node文件:這是用C/C++編寫的拓展文件,經過dlopen()方法加載最後編譯生成的文件

3).json文件:經過fs模塊同步讀取文件後,用JSON.parse()解析返回結果

4)其他拓展名文件:它們都被看成.js文件載入

Tip: 每個編譯成功的模塊都會將文件路徑做爲索引緩存在Module._cache對象上,爲了提升二次引入的性能。

根據不一樣的文件拓展名,Node會調用不一樣的讀取方式,例如.json文件調用:

// Native extension for .json
Module._extensions['.json'] = function(module, filename) {
var content = NativeModule.require('fs').readFileSync(filename, 'utf8'); try { module.exports = JSON.parse(stripBOM(content)); } catch (err) { err.message = filename + ': ' + err.message; throw err; } };
Module._extensions會被賦值給require()的extensions屬性
console.log(require.extensions);
//{ '.js': [Function], '.json': [Function], '.node': [Function]

 2.4 Javascript核心模塊的編譯過程

1)編譯JavaScript核心模塊

    lib目錄下的全部模塊文件也沒有定義require、module、export這些變量。在引入javascript核心模塊的過程當中,也經歷了頭尾包裝的過程,而後才執行和導出exports對象。與文件模塊有區別的地方在於:獲取源代碼的方式(核心模塊是從內存中加載的)以及緩存執行結果的位置。

function NativeModule(id) { this.filename = id + '.js'; this.id = id;
this.exports = {}; this.loaded = false;
}
NativeModule._source = process.binding('natives'); NativeModule._cache = {};

    Javascript核心模塊源文件經過process.binding('natives')取出,編譯成功的模塊緩存到NativeModule._cache對象上,文件模塊則緩存到Module._cache對象上。

 3.包與NPM

    具體講內容前,咱們先看一張圖:

 

 

 CommonJS的包規範定義很簡單,由包結構和包描述組成。

1)包結構:用於組織包中的各類文件

2)包描述:用於描述包的相關信息,供外部讀取分析

 

3.1包結構

    包其實是一個存檔文件,一個目錄直接打包爲.zip或tar.gz格式的文件,安裝解壓還原爲目錄。

符合CommonJS規範的包目錄應該包含如下文件:

1)package.json:包描述文件

2)bin: 用於存放可執行二進制文件的目錄

3)lib:用於存放JavaScript代碼的目錄

4)doc:用於存放文檔的目錄

5)test:用於存放單元測試用例的代碼

Tip: 當一個包完成後,用戶看到單元測試和文檔時,會有一種可靠的感受。

3.2.包描述文件於NPM

    包描述文件用於表達非代碼相關的信息,它是一個JSON格式的文件----package.json,位於包的根目錄下,是包的重要組成部分。NPM的全部行爲都與包描述文件的字段息息相關。

3.3NPM經常使用功能

     CommonJS包規範是理論,NPM是其中的一種實踐。NPM幫助第三方模塊的發佈、安裝和依賴等。藉助NPM,Node與第三方模塊之間造成來很好的一個生態系統。

    藉助NPM,咱們能夠快速安裝和管理依賴包。除此以外,NPM還有一些巧妙的用法。

// 查看當前NPM版本
$ npm -v 
1.2.32

// 查看幫助
$ npm

// 查看具體命令
npm help <command

3.4安裝依賴包

   npm install 安裝依賴包是NPM最多見的用法,執行這個命令後,NPM會在當前目錄下建立node_modules目錄,而後在node_modules目錄下建立express目錄,接着將包解壓到這個目錄下。

    安裝完成後,在代碼裏面使用require()來使用就能夠了。

    安裝有幾種模式,全局模式安裝、本地安裝、非官方源安裝。

3.5NPm鉤子命令

    package.json中script字段就是讓包在安裝或者卸載中提供鉤子機制,例如:

"scripts": {
  "preinstall": "preinstall.js",
  "install": "install.js", 
  "uninstall": 
  "uninstall.js", 
  "test": "test.js"
 }

    當咱們執行npm install <package>時,preinstall指向的腳本將會被加載執行,而後install指向的腳本會被執行。在執行npm uninstall <package> 時,uninstall指向的腳本也許會作一些清理工做等。

    當咱們在某一個具體的包目錄下執行npm test時,將會運行test指向的腳本。

 

總結

    Node經過模塊規範,造成了自身的原生模塊。NPM經過對包規範的支持,造成了第三方模塊,讓咱們在開發項目依賴獲得很好的解決,而且提供了分享和傳播的平臺。藉助第三方的開源力量,Node第三方模塊的發展速遞可謂是一步千里。

 

參考資源:

http://www.commonjs.org
http://npmjs.org/doc/README.html
http://www.infoq.com/cn/articles/msh-using-npm-manage-node.js-dependence
http://nodejs.org/docs/latest/api/modules.html
http://addyosmani.com/writing-modular-js/
http://seajs.org/docs/
http://zh.wikipedia.org/zh/JavaScript
http://zh.wikipedia.org/wiki/ECMAScript
http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
http://www.w3.org/TR/html5/
http://arstechnica.com/web/news/2009/12/commonjs-effort-sets-javascript-on-path-for-world-d
omination.ars
http://cnodejs.org/topic/4f16442ccae1f4aa270010d7
http://wiki.commonjs.org/wiki/Packages/1.0
http://npmjs.org/doc/developers.html#The-package-json-File

 

 

歡迎關注公衆號,進一步技術交流:

相關文章
相關標籤/搜索