什么是 npm —— 寫給初學(xué)者的編程教程

本篇文章可以作為 npm(Node.js 最喜歡的伙伴)的一個(gè)基本學(xué)習(xí)指南。
自 2009 年以來,Node.js 一直席卷全球。成千上萬個(gè)系統(tǒng)基于 Node.js 構(gòu)建,促使開發(fā)者在社區(qū)宣稱“JavaScript 正在吞噬軟件”。
Node 成功的主要因素之一是它廣受歡迎的軟件包管理器——npm,因?yàn)?npm 使 JavaScript 開發(fā)人員可以快速方便地共享軟件包, 例如 lodash 和?moment。
在我撰寫這篇文章時(shí),npm 已幫助發(fā)布了 130 萬個(gè)軟件包,每周下載量超過 160 億次!這些數(shù)字對(duì)于任何軟件工具來說都非常厲害。所以,現(xiàn)在讓我們討論一下 npm 到底是什么。
NPM 是什么
npm(“Node 包管理器”)是 JavaScript 運(yùn)行時(shí) Node.js 的默認(rèn)程序包管理器。
它也被稱為“Ninja Pumpkin Mutants”,“Nonprofit Pizza Makers”,以及許多其他隨機(jī)名稱,你可以在 npm-expansions 上探索這些名稱。
npm 由兩個(gè)主要部分組成:
用于發(fā)布和下載程序包的 CLI(命令行界面)工具 托管 JavaScript 程序包的在線存儲(chǔ)庫
為了更直觀地解釋,我們可以將存儲(chǔ)庫 npmjs.com 視為一個(gè)物流集散中心,該中心從賣方(npm 包裹的作者)那里接收貨物的包裹,并將這些貨物分發(fā)給買方(npm 包裹的用戶)。
為了促進(jìn)此過程,npmjs.com 物流集散中心雇用了一群勤勞的袋熊(npm CLI),他們將被分配給每個(gè) npmjs.com 用戶作為私人助理。因此,dependencies(依賴項(xiàng))會(huì)如下傳遞給 JavaScript 開發(fā)人員:

發(fā)布 JS 軟件包的過程如下:

讓我們看看這只袋熊如何協(xié)助想要在項(xiàng)目中使用 JavaScript 包的開發(fā)人員。下面我們還將看到它們(npm CLI)如何幫助開源向?qū)⑵涑錾膸焱葡蚴澜纭?/p>
package.json
每個(gè) JavaScript 項(xiàng)目(無論是 Node.js 還是瀏覽器應(yīng)用程序)都可以被當(dāng)作 npm 軟件包,并且通過 package.json ?來描述項(xiàng)目和軟件包信息。
我們可以將 package.json ?視為快遞盒子上的運(yùn)輸信息。
當(dāng)運(yùn)行 npm init ?初始化 JavaScript/Node.js 項(xiàng)目時(shí),將生成 package.json ?文件,文件內(nèi)的內(nèi)容(基本元數(shù)據(jù))由開發(fā)人員提供:
name:JavaScript 項(xiàng)目或庫的名稱。version:項(xiàng)目的版本。通常,在應(yīng)用程序開發(fā)中,由于沒有必要對(duì)開源庫進(jìn)行版本控制,因此經(jīng)常忽略這一塊。但是,仍可以用它來定義版本。description:項(xiàng)目的描述。license:項(xiàng)目的許可證。
npm scripts
package.json ?還支持一個(gè) scripts ?屬性,可以把它當(dāng)作在項(xiàng)目本地運(yùn)行的命令行工具。例如,一個(gè) npm 項(xiàng)目的 scripts部分可能看起來像這樣:
{
??"scripts":?{
????"build":?"tsc",
????"format":?"prettier?--write?**/*.ts",
????"format-check":?"prettier?--check?**/*.ts",
????"lint":?"eslint?src/**/*.ts",
????"pack":?"ncc?build",
????"test":?"jest",
????"all":?"npm?run?build?&&?npm?run?format?&&?npm?run?lint?&&?npm?run?pack?&&?npm?test"
??}
}
eslint,prettier,ncc,jest ?不是安裝為全局可執(zhí)行文件,而是安裝在項(xiàng)目本地的 ?node_modules/.bin/ ?中。
最新引入的 npx ?使我們可以像在全局安裝程序一樣運(yùn)行這些 node_modules ?項(xiàng)目作用域命令,方法是在其前面加上 npx ...(即npx prettier --write ** / *。ts)。
dependencies vs devDependencies
這兩個(gè)以鍵值對(duì)象的形式出現(xiàn),其中 npm 庫的名稱為鍵,其語義格式版本為值。大家可以看看?Github 的 TypeScript 操作模板中的示例:
{
??"dependencies":?{
????"@actions/core":?"^1.2.3",
????"@actions/github":?"^2.1.1"
??},
??"devDependencies":?{
????"@types/jest":?"^25.1.4",
????"@types/node":?"^13.9.0",
????"@typescript-eslint/parser":?"^2.22.0",
????"@zeit/ncc":?"^0.21.1",
????"eslint":?"^6.8.0",
????"eslint-plugin-github":?"^3.4.1",
????"eslint-plugin-jest":?"^23.8.2",
????"jest":?"^25.1.0",
????"jest-circus":?"^25.1.0",
????"js-yaml":?"^3.13.1",
????"prettier":?"^1.19.1",
????"ts-jest":?"^25.2.1",
????"typescript":?"^3.8.3"
??}
}
這些依賴通過帶有 --save ?或 --save-dev ?標(biāo)志的 npm install ?命令安裝。它們分別用于生產(chǎn)和開發(fā)/測試環(huán)境。在下一節(jié)中,我們將更深入地研究這些軟件包的安裝。
同時(shí),理解語義版本前面的符號(hào)非常重要(假設(shè)你已經(jīng)閱讀?semver 的 major.minor.patch ?模型):
^:表示最新的次版本,例如, ?^1.0.4?可能會(huì)安裝主版本系列1?的最新次版本 ?1.3.0。?:表示最新的補(bǔ)丁程序版本,與^?類似, ??1.0.4?可能會(huì)安裝次版本系列 ?1.0?的最新次版本1.0.7。
所有這些確切的軟件包版本都將記錄在 package-lock.json ?文件中。
package-lock.json
該文件描述了 npm JavaScript 項(xiàng)目中使用的依賴項(xiàng)的確切版本。如果 package.json ?是通用的描述性標(biāo)簽,則 package-lock.json ?是成分表。
就像我們通常不會(huì)讀取食品包裝袋上的成分表(除非你太無聊或需要知道)一樣,package-lock.json ?并不會(huì)被開發(fā)人員一行一行進(jìn)行讀取。
package-lock.json ?通常是由 npm install ?命令生成的,也可以由我們的 NPM CLI 工具讀取,以確保使用 npm ci ?復(fù)制項(xiàng)目的構(gòu)建環(huán)境。
用戶如何使用 NPM
從前面提到的 130 萬個(gè)發(fā)布的軟件包中,有 160 億次下載,可以推斷出,大多數(shù) npm 用戶都朝這個(gè)方向使用 npm。所以,了解如何使用這個(gè)強(qiáng)大的工具會(huì)很有幫助。
npm install
這是現(xiàn)在我們開發(fā) JavaScript/Node.js 應(yīng)用程序時(shí)最常用的命令。
默認(rèn)情況下,npm install ?將安裝帶有 ^ ?版本號(hào)的軟件包的最新版本。npm 項(xiàng)目上下文中的 npm install ?將根據(jù) package.json ?規(guī)范將軟件包下載到項(xiàng)目的 node_modules ?文件夾中,從而升級(jí)軟件包的版本(并重新生成 package-lock.json)。?npm install ?可以基于 ^ ?和 ? ?版本匹配。
如果要在全局上下文中安裝程序包,可以在機(jī)器的任何地方使用它,則可以指定全局標(biāo)志 -g(例如 live-server)。
npm 使安裝 JavaScript 軟件包非常容易,以至于經(jīng)常錯(cuò)誤地使用此命令。導(dǎo)致一些程序員對(duì) npm 開這樣的玩笑:

但是,npm 包太大、太深這樣的問題可以通過 --production ?標(biāo)志來拯救!在上一節(jié)中,我們討論了分別用于生產(chǎn)和開發(fā)/測試環(huán)境的 dependencies ?和 devDependencies ?。這個(gè) --production ?標(biāo)志是如何在 node_modules ?中進(jìn)行區(qū)別的。
通過將此標(biāo)志附加到 npm install ?命令,我們將僅從 dependencies ?安裝軟件包,從而將 node_modules ?的大小大大減小到應(yīng)用程序正常運(yùn)行所必需的大小。——不應(yīng)該將 devDependencies ?引入生產(chǎn)環(huán)境!
npm ci
因此,如果 npm install --production ?對(duì)于生產(chǎn)環(huán)境是最佳選項(xiàng),那么是否必須有一個(gè)對(duì)本地環(huán)境,測試環(huán)境最合適的選項(xiàng)?
答案是 npm ci。
就像如果 package_lock.json ?尚不存在于項(xiàng)目中一樣,無論何時(shí)調(diào)用 npm install ?都會(huì)生成它,npm ci ?會(huì)消耗該文件來下載項(xiàng)目所依賴的每個(gè)軟件包的確切版本。
這樣,無論是用于本地開發(fā)的筆記本電腦還是 Github Actions 等 CI(持續(xù)集成)構(gòu)建環(huán)境,我們都可以確保項(xiàng)目上下文在不同機(jī)器上保持完全相同。
npm audit
隨著越來越多的軟件包發(fā)布,并且易于安裝,因此 npm 軟件包容易受到惡意作者的惡意攻擊,例如這些。
意識(shí)到生態(tài)系統(tǒng)存在問題,npm.js 組織提出了 npm audit ?的主意。他們維護(hù)了一個(gè)安全漏洞列表,開發(fā)人員可以使用 npm audit ?命令來審核項(xiàng)目中的依賴項(xiàng)。
npm audit ?為開發(fā)人員提供了有關(guān)漏洞以及是否有要修復(fù)的版本的信息,例如:

如果補(bǔ)救措施在下一個(gè)不間斷的版本升級(jí)中可用,則可以使用 npm audit fix 來自動(dòng)升級(jí)受影響的依賴項(xiàng)的版本。
作者如何使用 NPM
我們已經(jīng)了解了作為用戶,如何通過 NPM CLI 有效得使用 NPM,但是作為作者又如何使用呢?
npm publish
將軟件包發(fā)送到 npmjs.com 非常容易,因?yàn)槲覀冎恍枰\(yùn)行 npm publish ?。棘手的部分(并非專門針對(duì) npm 軟件包作者)是確定軟件包的版本。
根據(jù) semver.org 的經(jīng)驗(yàn)法則:
當(dāng)你進(jìn)行不兼容的 API 更改時(shí)使用 MAJOR 版本 以向后兼容的方式添加功能時(shí)使用 MINOR 版本 進(jìn)行向后兼容的 bug 修復(fù)時(shí)使用 PATCH 版本
在發(fā)布軟件包時(shí),遵循上述規(guī)則尤為重要,可以確保你不會(huì)破壞任何人的代碼,因?yàn)?npm 中匹配的默認(rèn)版本是^(也就是下一個(gè)次版本)。
?? npm ?? JavaScript ?? Node.js ??
了解了以上知識(shí),我們就可以開始有效地使用 npm 并指揮可愛的袋熊大軍啦!

掃碼關(guān)注公眾號(hào),訂閱更多精彩內(nèi)容。

