Web1.0時代,JavaScript腳本語言的兩個主要功能:前端
Web2.0時代,前端工程師利用JavaScript大大提高了網頁的用戶體驗,經歷了工具類庫、組件庫、前端框架、前端應用的變遷。node
JavaScript的先天缺陷:模塊。express
高級語言的模塊化機制:npm
commonJS的願景:但願JavaScript可以在任何地方運行。json
JavaScript規範的缺陷:後端
CommonJS規範中,CommonJSAPI能夠編寫的應用:數組
模塊引用瀏覽器
採用require()方法;緩存
var math = require('math');
模塊定義安全
require():用來引入外部模塊;
exports:導出模塊的方法或變量,惟一導出的出口;
module:表明模塊自身;
// math.js exports.add = function (){ var sum = 0, i = 0, args = arguments, l = args.lenght; while(i<l){ sum += args[i++]; } return sum; } //program.js var math = require("math"); exports.increment = function (val){ return math.add(val,1); }
模塊標識:
就是傳遞給require()方法的參數,符合小駝峯命名的字符串,或者以.、..開頭的相對路徑或絕對路徑,能夠沒有後綴.js。
Node中引入模塊經歷的三個步驟:
Node中,模塊分爲兩種:
核心模塊部分在Node源代碼的編譯過程當中,編譯了二進制執行文件。在Node進程啓動時,部分核心模塊被直接加載進內存中,因此這部分核心模塊引入時,文件定位和編譯執行兩個步驟能夠省略掉,而且在路徑分析中優先判斷,它的加載速度是最快的。
文件模塊在運行時動態加載,須要完整的路徑分析、文件定位、編譯執行過程,速度比核心模塊慢。
與前端瀏覽器會緩存靜態腳本文件以提升性能同樣,Node對引入過的模塊都會進行緩存,以減小二次引入的開銷。不一樣的是,瀏覽器僅緩存文件,而Node緩存的是編譯和執行後的對象。
不管是核心模塊仍是文件模塊,require()方法對相同模塊的二次加載都一概採用緩存優先的方式,這是第一優先級的。不一樣之處在於核心模塊的緩存檢查先於文件模塊的緩存檢查。
模塊標識符分析
模塊標識符在Node中的分類:
核心模塊
核心模塊的優先級僅次於緩存加載,在Node的源代碼編譯過程當中,已經編譯爲二進制代碼了,其加載過程最快。
路徑形式的文件模塊
在分析路徑模塊時,require()將路徑轉化爲真實路徑,以真實路徑做爲索引,將編譯執行後的結果放在緩存中,以使二次加載更快,其加載速度慢於核心模塊。
自定義模塊
這類模塊的查找最費時,也是全部方式最慢的一種。
模塊路徑:
Node在定位文件模塊的具體文件時制定的查找策略,具體表現爲一個路徑組成的數組。
從緩存加載的優化策略使得二次引入時不須要路徑分析、文件定位和編譯執行的過程,大大提升了再次加載模塊時的效率。
文件定位過程當中,須要注意的細節,包括文件擴展名的分析、目錄和包的處理。
文件擴展名分析
require()在分析標識符的過程當中,出現標識符中不包含文件擴展名的狀況,Node會按.js、.json、.node的次序補足擴展名,依次嘗試。
在嘗試的過程當中,須要調用fs模塊同步阻塞式低判斷文件是否存在。
目錄分析和包
在分析標識的過程當中,require()經過分析文件擴展名以後,可能沒有查找到對應的文件,但卻獲得一個目錄,此時Node會將目錄當作一個包來處理。
在Node中,每一個文件模塊都是一個對象。
編譯和執行時引入文件模塊的最後一個階段。定位到具體的文件後,Node會新建一個模塊對象,而後根據路徑載入並編譯。對於不一樣的文件擴展名,載入方法也有所不一樣。
核心模塊分爲兩部分:
JavaScript核心模塊的編譯過程:
在此過程當中,JavaScript代碼已字符串的形式存儲在Node的命名空間中,是不可直接執行的。在啓動Node進程時,JavaScript代碼直接加載進內存中。在加載的過程當中,JavaScript核心模塊經歷標識符分析後,直接定位到內存中,比普通的文件模塊從磁盤中一處一處查找要快得多。
在引入JavaScript核心模塊的過程當中,也經歷了頭尾包裝的過程,而後才執行和導出exports對象。與文件模塊區別的地方在於:獲取源碼的方式(核心模塊從內存中加載)以及緩存執行結果的位置。
C/C++模塊主內完成核心,JavaScript主外實現封裝的模式是Node可以提升性能的常見方式。
內建模塊的組織形式;
內建模塊的優點:
在Node的全部模塊類型中,存在一種依賴層級關係:
文件模塊可能依賴核心模塊,核心模塊可能依賴內建模塊。
核心模塊的引入流程經歷了C/C++層面的內建模塊的定義,(JavaScript)核心模塊的定義和引入以及(JavaScript)文件模塊層面的引入。
核心模塊被編譯進二進制文件須要遵循必定規則。做爲Node的使用者,幾乎沒有機會參與核心模塊的開發。
JavaScript的一個典型的弱點就是位運算。
在JavaScript應用中,會頻繁出現位運算的需求,包括轉換、編碼等過程,經過JavaScript實現,CPU資源會耗費不少。
普通的擴展模塊與內建模塊的區別在於無須將源代碼編譯進Node,而是經過dlopen()方式動態加載。
經過GYP工具實現。
require()方法經過解析標識符、路徑分析、文件定位,而後加載執行便可。
C/C++擴展模塊與JavaScript模塊的區別在於加載以後不須要編譯,子類執行以後就能夠被外部調用了,其加載速度比JavaScript模塊速度略快。
使用C/C++擴展模塊的一個好處在於能夠更加靈活和動態地加載它們,保持Node模塊自身簡單性的同時,給予Node五=無限的可能性。
C/C++內建模塊:屬於最底層的模塊,屬於核心模塊,主要經過API給JavaScript核心模塊和第三方JavaScript文件模塊的調用。
JavaScript核心模塊的兩個職責:
文件模塊:一般由第三方編寫,包括普通JavaScript模塊的C/C++擴展模塊,主要調用方向爲普通JavaScript模塊調用擴展模塊。
包和NPM是將模塊聯繫起來的一種機制。
包組織模塊示意圖:
CommonJS包的定義:
由包結構和包描述文件兩個部分組成,前者用於組織包中的各類文件,後者用於描述包的相關信息,以供外部讀取分析。
包實際是一個存檔文件,即一個目錄直接打包爲.zip和tar.gz格式的文件,安裝後解壓還原爲目錄。
包目錄包含的文件:
包描述文件用於表達非代碼相關的信息,是個JSON格式的文件-package.json,位於包的根目錄下,是包的重要組成部分。
對於Node而言,NPM幫助完成了第三方模塊的發佈、安裝和依賴等。藉助Node與第三方模塊之間造成了很好的一個生態系統。
查看幫助;
查看當前NPM版本:
$ npm -v
查看幫助:
$ npm
安裝依賴包
安裝依賴包是NPM最多見的用法,執行語句:
$ npm install express
1.全局模式安裝
$ npm install express -g
2.從本地安裝
本地安裝只需爲NPM指明package.json文件的所在的位置便可。它能夠是一個包含package.json的存檔文件,也能夠是個URL地址,也能夠是個目錄有package.json的目錄的位置。
3.從非官方源安裝
從非官方安裝,能夠經過鏡像源安裝。
$ npm config set underscore --registry=http://registry.url
鏡像源安裝指定默認源:
$ npm config set registry http://registry.url
C/C++模塊其實是編譯後才能使用的。package.json中script字段的提出就是讓包在安裝或者卸載等過程當中提供鉤子機制。
發佈包
初始化包描述文件;
$ npm init
註冊包倉庫帳號
$ npm adduser
上傳包
$ npm publish .
安裝包
$ npm install hello_test_jackson --registy=http://npmjs.org
管理包權限
多人進行發佈
$ npm owner ls eventproxy
分析包
在使用NPM的過程當中,或許你不能確認當前目錄下可否經過require()順利引入想要的包,執行npm ls分析包。
$ npm ls
爲了同時可以享受NPM上衆多的包,同時對本身的包進行保密和限制,現有的解決方案就是企業搭建本身的NPM倉庫。
企業混合使用官方倉庫和局域倉庫的示意圖:
對於企業內部而言,私有的可重用模塊能夠打包到局域NPM倉庫,這樣能夠保持更新的中心化,不至於讓各個小項目各自維護相同功能的模塊,杜絕經過複製粘貼實現代碼共享的行爲。
NPM的潛在問題:
對於包的使用者而言,包質量和安全問題須要做爲是否採納模塊的一個判斷條件。
如何評判一個包的安全和質量?
Kwalitee模塊的考察點:
先後端JavaScript分別擱置在HTTP的兩端,它們扮演的角色並不一樣。
瀏覽器端的JavaScript:
須要經歷從同一個服務器端分發到多個客戶端執行,瓶頸在於帶寬,須要網絡加載代碼;
瀏覽器端的JavaScript:
相同的代碼須要屢次執行,瓶頸在於CPU和內存等資源,從磁盤中加載代碼;
二者的加載的速度不在一個數量級別。
CommonJS爲後端JavaScript制定的規範;
AMD爲前端JavaScript制定的規範;
AMD規範是CommonJS模塊規範的一個延伸,定義以下;
define(id?,dependenceies?,factory)
模塊的id和依賴是可選的,
與Node模塊類似之處:
factory的內容就是實際代碼的內容;
與Node模塊不一樣之處:
CMD規範由國內的玉伯提出,與AMD規範的主要區別在於定義模塊和依賴引入的部分。
AMD須要在聲明模塊的時候指定全部的依賴,經過形參傳遞到模塊內容;
define(['dep1','dep2'],function (dep1,dep2){ return function (){}; })
爲了讓同一個模塊能夠運行在先後端,須要考慮兼容前端也實現了模塊規範的環境。爲了保持先後端的一致性,類庫開發者須要將類庫代碼包裝在一個閉包內。