欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

學(xué)習(xí)Node.js模塊機(jī)制

 更新時(shí)間:2016年10月17日 14:32:57   作者:沈鴻斌  
這篇文章主要為大家詳細(xì)介紹了Node.js模塊機(jī)制,一篇關(guān)于Node.js模塊機(jī)制的學(xué)習(xí)筆記,感興趣的小伙伴們可以參考一下

一、CommonJS的模塊規(guī)范


Node與瀏覽器以及 W3C組織、CommonJS組織、ECMAScript之間的關(guān)系

Node借鑒CommonJS的Modules規(guī)范實(shí)現(xiàn)了一套模塊系統(tǒng),所以先來看看CommonJS的模塊規(guī)范。

CommonJS對模塊的定義十分簡單,主要分為模塊引用、模塊定義和模塊標(biāo)識3個(gè)部分。

1. 模塊引用

模塊引用的示例代碼如下:

var math = require('math');

在CommonJS規(guī)范中,存在require()方法,這個(gè)方法接受模塊標(biāo)識,以此引入一個(gè)模塊的API到當(dāng)前上下文中。

2. 模塊定義

在模塊中,上下文提供require()方法來引入外部模塊。對應(yīng)引入的功能,上下文提供了exports對象用于導(dǎo)出當(dāng)前模塊的方法或者變量,并且它是唯一導(dǎo)出的出口。在模塊中,還存在一個(gè)module對象,它代表模塊自身,而exports是module的屬性。在Node中,一個(gè)文件就是一個(gè)模塊,將方法掛載在exports對象上作為屬性即可定義導(dǎo)出的方式:

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

在另一個(gè)文件中,我們通過require()方法引入模塊后,就能調(diào)用定義的屬性或方法了:

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

3.模塊標(biāo)識

模塊標(biāo)識其實(shí)就是傳遞給require()方法的參數(shù),它必須是符合小駝峰命名的字符串,或者以.、..開頭的相對路徑,或者絕對路徑。它可以沒有文件名后綴.js。模塊的定義十分簡單,接口也十分簡潔。它的意義在于將類聚的方法和變量等限定在私有的作用域中,同時(shí)支持引入和導(dǎo)出功能以順暢地連接上下游依賴。每個(gè)模塊具有獨(dú)立的空間,它們互不干擾,在引用時(shí)也顯得干凈利落。

二、Node的模塊實(shí)現(xiàn)

Node在實(shí)現(xiàn)中并非完全按照規(guī)范實(shí)現(xiàn),而是對模塊規(guī)范進(jìn)行了一定的取舍,同時(shí)也增加了少許自身需要的特性。盡管規(guī)范中exports、require和module聽起來十分簡單,但是Node在實(shí)現(xiàn)它們的過程中究竟經(jīng)歷了什么,這個(gè)過程需要知曉。
在Node中引入模塊,需要經(jīng)歷如下3個(gè)步驟。

1. 路徑分析

2. 文件定位

3. 編譯執(zhí)行

在Node中,模塊分為兩類:一類是Node提供的模塊,稱為核心模塊;另一類是用戶編寫的模塊,稱為文件模塊。

•  核心模塊部分在Node源代碼的編譯過程中,編譯進(jìn)了二進(jìn)制執(zhí)行文件。在Node進(jìn)程啟動時(shí),部分核心模塊就被直接加載進(jìn)內(nèi)存中,所以這部分核心模塊引入時(shí),文件定位和編譯執(zhí)行這兩個(gè)步驟可以省略掉,并且在路徑分析中優(yōu)先判斷,所以它的加載速度是最快的。

•  文件模塊則是在運(yùn)行時(shí)動態(tài)加載,需要完整的路徑分析、文件定位、編譯執(zhí)行過程,速度比核心模塊慢。

1.優(yōu)先從緩存加載

與前端瀏覽器會緩存靜態(tài)腳本文件以提高性能一樣,Node對引入過的模塊都會進(jìn)行緩存,以減少二次引入時(shí)的開銷。不同的地方在于,瀏覽器僅僅緩存文件,而Node緩存的是編譯和執(zhí)行之后的對象。不論是核心模塊還是文件模塊,require()方法對相同模塊的二次加載都一律采用緩存優(yōu)先的方式,這是第一優(yōu)先級的。不同之處在于核心模塊的緩存檢查先于文件模塊的緩存檢查。

2.路徑分析和文件定位

因?yàn)闃?biāo)識符有幾種形式,對于不同的標(biāo)識符,模塊的查找和定位有不同程度上的差異。

1). 模塊標(biāo)識符分析
Node基于一個(gè)模塊標(biāo)識符進(jìn)行模塊查找。模塊標(biāo)識符在Node中主要分為以下幾類。

核心模塊,如http、fs、path等。
.或..開始的相對路徑文件模塊。
以/開始的絕對路徑文件模塊。
非路徑形式的文件模塊,如自定義的connect模塊。

•  核心模塊

核心模塊的優(yōu)先級僅次于緩存加載,它在Node的源代碼編譯過程中已經(jīng)編譯為二進(jìn)制代碼,其加載過程最快。如果試圖加載一個(gè)與核心模塊標(biāo)識符相同的自定義模塊,那是不會成功的。如果自己編寫了一個(gè)http用戶模塊,想要加載成功,必須選擇一個(gè)不同的標(biāo)識符或者換用路徑的方式。

•  路徑形式的文件模塊

以.、..和/開始的標(biāo)識符,這里都被當(dāng)做文件模塊來處理。在分析路徑模塊時(shí),require()方法會將路徑轉(zhuǎn)為真實(shí)路徑,并以真實(shí)路徑作為索引,將編譯執(zhí)行后的結(jié)果存放到緩存中,以使二次加載時(shí)更快。由于文件模塊給Node指明了確切的文件位置,所以在查找過程中可以節(jié)約大量時(shí)間,其加載速度慢于核心模塊。

•  自定義模塊

自定義模塊指的是非核心模塊,也不是路徑形式的標(biāo)識符。它是一種特殊的文件模塊,可能是一個(gè)文件或者包的形式。這類模塊的查找是最費(fèi)時(shí)的,也是所有方式中最慢的一種。

2).文件定位

從緩存加載的優(yōu)化策略使得二次引入時(shí)不需要路徑分析、文件定位和編譯執(zhí)行的過程,大大提高了再次加載模塊時(shí)的效率。但在文件的定位過程中,還有一些細(xì)節(jié)需要注意,這主要包括文件擴(kuò)展名的分析、目錄和包的處理。

•  文件擴(kuò)展名分析

CommonJS模塊規(guī)范也允許在標(biāo)識符中不包含文件擴(kuò)展名,這種情況下,Node會按.js、.json、.node的次序補(bǔ)足擴(kuò)展名,依次嘗試。在嘗試的過程中,需要調(diào)用fs模塊同步阻塞式地判斷文件是否存在。因?yàn)镹ode是單線程的,所以這里是一個(gè)會引起性能問題的地方。小訣竅是:如果是.node和.json文件,在傳遞給require()的標(biāo)識符中帶上擴(kuò)展名,會加快一點(diǎn)速度。

•  目錄分析和包

在分析標(biāo)識符的過程中,require()通過分析文件擴(kuò)展名之后,可能沒有查找到對應(yīng)文件,但卻得到一個(gè)目錄,此時(shí)Node會將目錄當(dāng)做一個(gè)包來處理。

在這個(gè)過程中,Node對CommonJS包規(guī)范進(jìn)行了一定程度的支持。首先,Node在當(dāng)前目錄下查找package.json(CommonJS包規(guī)范定義的包描述文件),通過JSON.parse()解析出包描述對象,從中取出main屬性指定的文件名進(jìn)行定位。如果文件名缺少擴(kuò)展名,將會進(jìn)入擴(kuò)展名分析的步驟。而如果main屬性指定的文件名錯(cuò)誤,或者壓根沒有package.json文件,Node會將index當(dāng)做默認(rèn)文件名,然后依次查找index.js、index.node、index.json。

如果在目錄分析的過程中沒有定位成功任何文件,則自定義模塊進(jìn)入下一個(gè)模塊路徑進(jìn)行查找。如果模塊路徑數(shù)組都被遍歷完畢,依然沒有查找到目標(biāo)文件,則會拋出查找失敗的異常。

3).模塊編譯
在Node中,每個(gè)文件模塊都是一個(gè)對象,它的定義如下:

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 = []; 
}  

編譯和執(zhí)行是引入文件模塊的最后一個(gè)階段。定位到具體的文件后,Node會新建一個(gè)模塊對象,然后根據(jù)路徑載入并編譯。對于不同的文件擴(kuò)展名,其載入方法也有所不同,具體如下所示。

•  .js文件。

通過fs模塊同步讀取文件后編譯執(zhí)行。

•  .node文件。

這是用C/C++編寫的擴(kuò)展文件,通過dlopen()方法加載最后編譯生成的文件。

•  .json文件。

通過fs模塊同步讀取文件后,用JSON.parse()解析返回結(jié)果。

•  其余擴(kuò)展名文件。

它們都被當(dāng)做.js文件載入。

每一個(gè)編譯成功的模塊都會將其文件路徑作為索引緩存在Module._cache對象上,以提高二次引入的性能。

JavaScript模塊的編譯

回到CommonJS模塊規(guī)范,我們知道每個(gè)模塊文件中存在著require、exports、module這3個(gè)變量,但是它們在模塊文件中并沒有定義,那么從何而來呢?甚至在Node的API文檔中,我們知道每個(gè)模塊中還有__filename、__dirname這兩個(gè)變量的存在,它們又是從何而來的呢?如果我們把直接定義模塊的過程放諸在瀏覽器端,會存在污染全局變量的情況。

事實(shí)上,在編譯的過程中,Node對獲取的JavaScript文件內(nèi)容進(jìn)行了頭尾包裝。在頭部添加了(function (exports, require, module, __filename, __dirname) {\n,在尾部添加了\n});。一個(gè)正常的JavaScript文件會被包裝成如下的樣子:

(function (exports, require, module, __filename, __dirname) {
 var math = require('math');
 exports.area = function (radius) {
  return Math.PI * radius * radius;
 };
});

這樣每個(gè)模塊文件之間都進(jìn)行了作用域隔離。包裝之后的代碼會通過vm原生模塊的runInThisContext()方法執(zhí)行(類似eval,只是具有明確上下文,不污染全局),返回一個(gè)具體的function對象。最后,將當(dāng)前模塊對象的exports屬性、require()方法、module(模塊對象自身),以及在文件定位中得到的完整文件路徑和文件目錄作為參數(shù)傳遞給這個(gè)function()執(zhí)行。

3.包和NPM

在模塊之外,包和NPM則是將模塊聯(lián)系起來的一種機(jī)制。

CommonJS的包規(guī)范的定義其實(shí)也十分簡單,它由包結(jié)構(gòu)和包描述文件兩個(gè)部分組成,前者用于組織包中的各種文件,后者則用于描述包的相關(guān)信息,以供外部讀取分析。

1.包結(jié)構(gòu)

包實(shí)際上是一個(gè)存檔文件,即一個(gè)目錄直接打包為.zip或tar.gz格式的文件,安裝后解壓還原為目錄。完全符合CommonJS規(guī)范的包目錄應(yīng)該包含如下這些文件。

package.json:包描述文件。
bin:用于存放可執(zhí)行二進(jìn)制文件的目錄。
lib:用于存放JavaScript代碼的目錄。
doc:用于存放文檔的目錄。
test:用于存放單元測試用例的代碼。

2.包描述文件

包描述文件用于表達(dá)非代碼相關(guān)的信息,它是一個(gè)JSON格式的文件——package.json,位于包的根目錄下,是包的重要組成部分。而NPM的所有行為都與包描述文件的字段息息相關(guān)。

這個(gè)可以看看NPM官網(wǎng)對package.json的定義規(guī)范。

可以通過npm adduser,  npm publish把自己的package上傳到npm倉庫。

三、題外話: AMD、CMD、兼容多種模塊規(guī)范的類庫

1. AMD

是CommonJS模塊規(guī)范的一個(gè)延伸,它的模塊定義如下:
define(id?, dependencies?, factory);

2.CMD

3.兼容

為了讓同一個(gè)模塊可以運(yùn)行在前后端,在寫作過程中需要考慮兼容前端也實(shí)現(xiàn)了模塊規(guī)范的環(huán)境。為了保持前后端的一致性,類庫開發(fā)者需要將類庫代碼包裝在一個(gè)閉包內(nèi)。以下代碼演示如何將hello()方法定義到不同的運(yùn)行環(huán)境中,它能夠兼容Node、AMD、CMD以及常見的瀏覽器環(huán)境中:

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 詳解Nodejs之npm&package.json

    詳解Nodejs之npm&package.json

    本篇文章主要介紹了詳解Nodejs之npm&package.json,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-06-06
  • mac下安裝node.js的詳細(xì)步驟

    mac下安裝node.js的詳細(xì)步驟

    Node.js本質(zhì)上是一個(gè)JavaScript運(yùn)行時(shí)環(huán)境,它提供了在服務(wù)器端運(yùn)行JavaScript代碼的能力,這篇文章主要給大家介紹了關(guān)于mac下安裝node.js的詳細(xì)步驟,需要的朋友可以參考下
    2023-10-10
  • nodejs中安裝ghost出錯(cuò)的原因及解決方法

    nodejs中安裝ghost出錯(cuò)的原因及解決方法

    本篇文章主要介紹了nodejs中安裝ghost出錯(cuò)的原因及解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • nodejs代碼執(zhí)行繞過的一些技巧匯總

    nodejs代碼執(zhí)行繞過的一些技巧匯總

    這篇文章主要給大家介紹了關(guān)于nodejs代碼執(zhí)行繞過的一些技巧,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • nodejs實(shí)現(xiàn)超簡單生成二維碼的方法

    nodejs實(shí)現(xiàn)超簡單生成二維碼的方法

    這篇文章主要介紹了nodejs實(shí)現(xiàn)超簡單生成二維碼的方法,結(jié)合實(shí)例形式分析了nodejs基于qr-image插件生成二維碼的相關(guān)操作技巧,需要的朋友可以參考下
    2018-03-03
  • node.js入門教程之querystring模塊的使用方法

    node.js入門教程之querystring模塊的使用方法

    querystring模塊主要用來解析查詢字符串,下面這篇文章主要介紹了關(guān)于node.js中querystring模塊使用方法的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-02-02
  • 詳解Nodejs 部署到阿里云全過程

    詳解Nodejs 部署到阿里云全過程

    這篇文章主要介紹了詳解Nodejs 部署到阿里云全過程,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2017-06-06
  • Nodejs模塊載入運(yùn)行原理

    Nodejs模塊載入運(yùn)行原理

    本篇文章給大家詳細(xì)分享了Nodejs模塊載入運(yùn)行原理知識點(diǎn),對此有興趣的朋友可以跟著小編一起學(xué)習(xí)下。
    2018-02-02
  • 使用Node.js給圖片加水印的方法

    使用Node.js給圖片加水印的方法

    使用Node.js給圖片加水印,首先要確保本地安裝了node環(huán)境。然后,我們進(jìn)行圖像編輯操作需要用到一個(gè)Node.js的庫:images。具體詳情大家可以通過本文了解下
    2016-11-11
  • 利用Node.js制作爬取大眾點(diǎn)評的爬蟲

    利用Node.js制作爬取大眾點(diǎn)評的爬蟲

    相信每位用過大眾點(diǎn)評的人都知道,大眾點(diǎn)評上有很多美食餐館的信息,所以這篇文章給大家分享利用Node.js實(shí)現(xiàn)爬取大眾點(diǎn)評的爬蟲,正好可以拿來練練手Node.js。感興趣的可以參考借鑒。
    2016-09-09

最新評論