開篇前,咱們先來看張圖, 看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
歡迎關注公衆號,進一步技術交流: