服務(wù)器端JavaScript運(yùn)行環(huán)境Node.js的依賴性管理
譯文【51CTO.com快譯】眾所周知,Node.js是一個基于Chrome V8引擎的服務(wù)器端JavaScript運(yùn)行環(huán)境。它采用了一種事件驅(qū)動的、非阻塞式的I/O模式,運(yùn)行起來既輕量級又高效。誠然,我們可以使用單個js文件,來編寫出應(yīng)用程序所涉及到的全部內(nèi)容,但這樣既不靈活,又不夠模塊化。而Node.js的出現(xiàn),讓模塊化代碼的編寫變得非常簡便。因此,對于Node.js的核心,我們需要理解和掌握的一個重要概念便是:依賴關(guān)系的管理。本文將和您一起探討依賴項管理的各種模式,以及Nodejs是如何加載依賴項的。
在深入探討細(xì)節(jié)之前,讓我們首先弄清楚什么是模塊。簡而言之,模塊是一段代碼。為了共享和重用,我們需要將代碼進(jìn)行分組放置。通過模塊,我們可以將復(fù)雜的應(yīng)用程序分解到小塊代碼中。同時,模塊也能夠幫助我們理解程序代碼的意圖,并發(fā)現(xiàn)或修復(fù)各種錯誤。如果您想深入了解有關(guān)JavaScript模塊系統(tǒng)的相關(guān)知識,請參見--https://hexquote.com/javascript-module-system/。
自2009年以來,CommonJS便實現(xiàn)了Javascript的模塊化規(guī)范。它規(guī)范了模塊的特性和各個模塊之間的相互依賴性。由于每個文件都被當(dāng)做一個模塊(通常,module變量代表了當(dāng)前模塊),而且有自己的作用域,因此每個文件里面的變量、函數(shù)、以及類,都是私有的,且對于其他模塊是不可見的。而模塊的exports屬性便是對外的接口。只有通過exports導(dǎo)出的屬性,才能被其他模塊識別和加載。而Node是基于CommonJs規(guī)范來實現(xiàn)模塊的同步與加載。也就是說,我們可以通過在模塊中調(diào)用require()方法,來接收模塊的標(biāo)識,并根據(jù)node的模塊引入規(guī)則,引入其他模塊,進(jìn)而調(diào)用對應(yīng)的屬性和方法。
在此,我假設(shè)您已經(jīng)掌握了Nodejs的上述基礎(chǔ)知識。當(dāng)然,如果您是一名Node.js的新手,則可以通過查看Node.js的相關(guān)簡介(https://dzone.com/articles/nodejs-introduction),來了解更多背景信息。
設(shè)置應(yīng)用
讓我們從最簡單的開始。假設(shè)我已經(jīng)為某個項目創(chuàng)建了一個目錄。通過運(yùn)用npm init命令對其初始化后,我們將創(chuàng)建app.js和appMsg.js,兩個JavaScript文件。下圖展示了本項目的目錄結(jié)構(gòu),我們將其作為管理的起點。如果您感興趣的話,可以從文末給出的git存儲庫鏈接中,下載該項目的最終源代碼。
默認(rèn)情況下,這兩個.js文件均為空。讓我們通過如下更改,來更新appMsgs.js文件。
上面的代碼段展示了module.exports關(guān)鍵字的用法。此語法用于公開給定文件(此處為appMsgs.js)中的屬性或?qū)ο螅员隳軌蛟诹硪粋€文件(如本例中的app.js)中被直接使用到。
在該系統(tǒng)中,每個文件都可以訪問到名為module.exports的文件。因此,我們在appMsgs.js文件中公開了一些項目,以方便觀察app.js是如何使用(require)某些屬性的。
顯然,require關(guān)鍵字可以方便我們引用某個文件。也就是說,當(dāng)我們執(zhí)行require時,它將返回一個代表著模塊化代碼段的對象。因此,我們可以將其分配給一個appMsgs變量,然后在console.log的語句中簡單地使用該屬性。當(dāng)代碼被執(zhí)行時,我們將看到如下輸出:
該require通過執(zhí)行JavaScript,構(gòu)造出一個具有某種功能函數(shù)的對象,作為返回。它們既可能是一個類構(gòu)造函數(shù),又可以是其中包含了許多元素、或一些簡單屬性的對象。針對不同的模式,我們既可以導(dǎo)出多個對象,又可以只導(dǎo)出那些復(fù)雜的對象??梢?,通過require和module.exports,我們可以創(chuàng)建出模塊化的應(yīng)用程序。
值得注意的是,應(yīng)用程序所需的功能函數(shù)只會僅加載代碼一次。也就是說,無論執(zhí)行了什么代碼,它們都不會被執(zhí)行第二次。那么,如果別的程序也要通過require來獲取對象的話,它將只能獲得該對象的緩存版本。
下面,讓我們來看看導(dǎo)出的方式。
如上面代碼段所示,我對前面的代碼進(jìn)行了更改?,F(xiàn)在,我不再公布對象了,而是導(dǎo)出了一個功能函數(shù)(function)。該函數(shù)在每次被調(diào)用時,都需要執(zhí)行該代碼。
下面,讓我們來看看如何在app.js文件中使用它:
更新app.js文件
除了調(diào)用某個屬性,我們還可以像執(zhí)行函數(shù)一樣去執(zhí)行它。因此,這里的區(qū)別主要是,每當(dāng)我們執(zhí)行該代碼時,函數(shù)內(nèi)部的代碼都會被重新執(zhí)行(re-executed)。
下面是我們重新運(yùn)行該代碼段的輸出:
至此,我們已經(jīng)看到了module.exports的兩種模式,及其兩者的區(qū)別。還有一個常見的模式是,將其用作構(gòu)造器方法(constructor method)。下面,讓我們再來看一個例子:
下面是更改過的app.js文件:
從本質(zhì)上講,這與您在JavaScript中創(chuàng)建偽類(pseudo-class),并且創(chuàng)建它的各種實例(instances)是一致的。
下面是更改后的輸出:
接著,讓我們接著討論此類模式的另一個示例。如下代碼段所示,我創(chuàng)建了一個名為userRepo.js的新文件。
下面是更改后的app.js文件。
下圖是該更改被執(zhí)行后的結(jié)果:
當(dāng)然,針對單個文件都去使用require的情況并不常見。接下來,讓我們再討論另一種模式--文件夾的依賴性。
文件夾依賴性
為了弄清Node.js是如何查找依賴性的,讓我們重溫一下前面例子中的JavaScript代碼:
- var appMsgs = require(“ ./appMsgs”)
Node不但會查找appMsgs.js文件,而且會查找作為目錄的appMsgs,并取出它的值。
我創(chuàng)建了一個名為logger的文件夾,并在其中創(chuàng)建了一個index.js文件,其內(nèi)容如下面的代碼段所示:
下面是require此模塊的app.js文件:
可見,在本例中,我們可以寫出這樣的JavaScript代碼:
- var logger = require(“./logger/index.js”)
上述較長的路徑形式肯定是正確的。但是,我們其實只需寫出如下的JavaScript代碼即可:
- var logger = require(“./logger”)
由于沒有l(wèi)ogger.js,而只有l(wèi)ogger目錄,因此在默認(rèn)情況下,Node將加載index.js作為logger的起點。我們可以通過如下命令,來驗證其輸出結(jié)果:
在此,您可能心生疑慮:我們?yōu)槭裁慈绱速M盡周折地創(chuàng)建文件夾和index.js呢?其背后的原因在于:您可能會將一些復(fù)雜的依賴項放在一起,而這些依賴項也可能還有其他的依賴項。而對于需要logger的調(diào)用者(caller)而言,它們不需要知道其他依賴項的存在。
這便是一種封裝形式(encapsulation)。我們完全可以在多個文件中,構(gòu)建更為復(fù)雜的代碼段;而在使用者(consumer)角度,它們只需使用一個文件足矣??梢?,文件夾是管理此類依賴性關(guān)系的更好方法。
Node程序包管理器(NPM)
第三類值得我們探討的依賴性管理是NPM。顧名思義,NPM是Node.js程序包的管理和分發(fā)工具,它相當(dāng)于后端的Maven。它可以讓Javascript開發(fā)者更加輕松的共享和共用代碼段。
通常,我們可以使用如下npm命令,來安裝依賴項:
- npm install underscore;
如下代碼段所示,我們也可以簡單地在app.js中require它:
如您所見,我們可以通過underscore的軟件包來使用各項功能。同理,當(dāng)需要用到此類模塊時,我們并沒有指定文件的路徑,而只需使用其名稱即可。Node.js將會從您的應(yīng)用程序的node_modules文件夾中,自動加載到其對應(yīng)的模塊。
下面是代碼執(zhí)行后的輸出結(jié)果:
小結(jié)
綜上所述,我們討論了Node.js是如何管理其依賴性關(guān)系的。您可以從此Git存儲庫—https://github.com/jawadhasan/nodedependency處,下載上述示例的源代碼。
原文標(biāo)題:Node.js – Dependency Management,作者: Jawad Hasan Shani
【51CTO譯稿,合作站點轉(zhuǎn)載請注明原文譯者和出處為51CTO.com】