国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

2018 年了,你還是只會(huì) npm install 嗎

libxd / 1942人閱讀

摘要:無(wú)需手動(dòng)拷貝文件或者創(chuàng)建軟鏈接到目錄,有更優(yōu)雅的解決方案。這是因?yàn)樽R(shí)別協(xié)議的,得知這個(gè)包需要直接從文件系統(tǒng)中獲取,會(huì)自動(dòng)創(chuàng)建軟鏈接到中,完成安裝過(guò)程。

nodejs 社區(qū)乃至 Web 前端工程化領(lǐng)域發(fā)展到今天,作為 node 自帶的包管理工具的 npm 已經(jīng)成為每個(gè)前端開(kāi)發(fā)者必備的工具。但是現(xiàn)實(shí)狀況是,我們很多人對(duì)這個(gè)nodejs基礎(chǔ)設(shè)施的使用和了解還停留在: 會(huì)用 npm install 這里(一言不合就刪除整個(gè) node_modules 目錄然后重新 install 這種事你沒(méi)做過(guò)嗎?)

當(dāng)然 npm 能成為現(xiàn)在世界上最大規(guī)模的包管理系統(tǒng),很大程度上確實(shí)歸功于它足夠用戶(hù)友好,你看即使我只會(huì)執(zhí)行 install 也不必太擔(dān)心出什么大岔子. 但是 npm 的功能遠(yuǎn)不止于 install 一下那么簡(jiǎn)單,這篇文章幫你扒一扒那些你可能不知道的 npm 原理、特性、技巧,以及(我認(rèn)為的)最佳實(shí)踐。

你懶得讀的 npm 文檔,我?guī)湍惴g然后試驗(yàn)整理過(guò)來(lái)了 ???

1. npm init

我們都知道 package.json 文件是用來(lái)定義一個(gè) package 的描述文件, 也知道npm init 命令用來(lái)初始化一個(gè)簡(jiǎn)單的 package.json 文件,執(zhí)行該命令后終端會(huì)依次詢(xún)問(wèn) name, version, description 等字段。

1.1 npm init 執(zhí)行默認(rèn)行為


而如果想要偷懶步免去一直按 enter,在命令后追加 --yes 參數(shù)即可,其作用與一路下一步相同。

npm init --yes

1.2 自定義 npm init 行為

npm init 命令的原理并不復(fù)雜,調(diào)用腳本,輸出一個(gè)初始化的 package.json 文件就是了。所以相應(yīng)地,定制 npm init 命令的實(shí)現(xiàn)方式也很簡(jiǎn)單,在 Home 目錄創(chuàng)建一個(gè) .npm-init.js 即可,該文件的 module.exports 即為 package.json 配置內(nèi)容,需要獲取用戶(hù)輸入時(shí)候,使用 prompt() 方法即可。

例如編寫(xiě)這樣的 ~/.npm-init.js

const desc = prompt("description?", "A new package...")
const bar = prompt("bar?", "")
const count = prompt("count?", "42")

module.exports = {
  key: "value",
  foo: {
    bar: bar,
    count: count
  },
  name: prompt("name?", process.cwd().split("/").pop()),
  version: prompt("version?", "0.1.0"),
  description: desc,
  main: "index.js",
}

此時(shí)在 ~/hello 目錄下執(zhí)行 npm init 將會(huì)得到這樣的 package.json:

{
  "key": "value",
  "foo": {
    "bar": "",
    "count": "42"
  },
  "name": "hello",
  "version": "0.1.0",
  "description": "A new package...",
  "main": "index.js"
}

除了生成 package.json, 因?yàn)?.npm-init.js 是一個(gè)常規(guī)的模塊,意味著我們可以執(zhí)行隨便什么 node 腳本可以執(zhí)行的任務(wù)。例如通過(guò) fs 創(chuàng)建 README, .eslintrc 等項(xiàng)目必需文件,實(shí)現(xiàn)項(xiàng)目腳手架的作用。

2. 依賴(lài)包安裝

依賴(lài)管理是 npm 的核心功能,原理就是執(zhí)行 npm install 從 package.json 中的 dependencies, devDependencies 將依賴(lài)包安裝到當(dāng)前目錄的 ./node_modules 文件夾中。

2.1 package定義

我們都知道要手動(dòng)安裝一個(gè)包時(shí),執(zhí)行 npm install 命令即可。這里的第三個(gè)參數(shù) package 通常就是我們所要安裝的包名,默認(rèn)配置下 npm 會(huì)從默認(rèn)的源 (Registry) 中查找該包名對(duì)應(yīng)的包地址,并下載安裝。但在 npm 的世界里,除了簡(jiǎn)單的指定包名, package 還可以是一個(gè)指向有效包名的 http url/git url/文件夾路徑。

閱讀 npm的文檔, 我們會(huì)發(fā)現(xiàn)package 準(zhǔn)確的定義,只要符合以下 a) 到 g) 其中之一條件,就是一個(gè) package:

# 說(shuō)明 例子
a) 一個(gè)包含了程序和描述該程序的 package.json 文件 的 文件夾 ./local-module/
b) 一個(gè)包含了 (a) 的 gzip 壓縮文件 ./module.tar.gz
c) 一個(gè)可以下載得到 (b) 資源的 url (通常是 http(s) url) https://registry.npmjs.org/we...
d) 一個(gè)格式為 @ 的字符串,可指向 npm 源(通常是官方源 npmjs.org)上已發(fā)布的可訪(fǎng)問(wèn) url,且該 url 滿(mǎn)足條件 (c) webpack@4.1.0
e) 一個(gè)格式為 @ 的字符串,在 npm 源上該指向某 得到 @,后者滿(mǎn)足條件 (d) webpack@latest
f) 一個(gè)格式為 的字符串,默認(rèn)添加 latest 標(biāo)簽所得到的 @latest 滿(mǎn)足條件 (e) webpack
g) 一個(gè) git url, 該 url 所指向的代碼庫(kù)滿(mǎn)足條件 (a) git@github.com:webpack/webpack.git
2.2 安裝本地包/遠(yuǎn)程git倉(cāng)庫(kù)包

上面表格的定義意味著,我們?cè)诠蚕硪蕾?lài)包時(shí),并不是非要將包發(fā)表到 npm 源上才可以提供給使用者來(lái)安裝。這對(duì)于私有的不方便 publish 到遠(yuǎn)程源(即使是私有源),或者需要對(duì)某官方源進(jìn)行改造,但依然需要把包共享出去的場(chǎng)景來(lái)說(shuō)非常實(shí)用。

場(chǎng)景1: 本地模塊引用

nodejs 應(yīng)用開(kāi)發(fā)中不可避免有模塊間調(diào)用,例如在實(shí)踐中經(jīng)常會(huì)把需要被頻繁引用的配置模塊放到應(yīng)用根目錄;于是在創(chuàng)建了很多層級(jí)的目錄、文件后,很可能會(huì)遇到這樣的代碼:

const config = require("../../../../config.js");

除了看上去很丑以外,這樣的路徑引用也不利于代碼的重構(gòu)。并且身為程序員的自我修養(yǎng)告訴我們,這樣重復(fù)的代碼多了也就意味著是時(shí)候把這個(gè)模塊分離出來(lái)供應(yīng)用內(nèi)其他模塊共享了。例如這個(gè)例子里的 config.js 非常適合封裝為 package 放到 node_modules 目錄下,共享給同應(yīng)用內(nèi)其他模塊。

無(wú)需手動(dòng)拷貝文件或者創(chuàng)建軟鏈接到 node_modules 目錄,npm 有更優(yōu)雅的解決方案。

方案:

創(chuàng)建 config 包:
新增 config 文件夾; 重命名 config.js 為 config/index.js 文件; 創(chuàng)建 package.json 定義 config 包

{
    "name": "config",
    "main": "index.js",
    "version": "0.1.0"
}

在應(yīng)用層 package.json 文件中新增依賴(lài)項(xiàng),然后執(zhí)行 npm install; 或直接執(zhí)行第 3 步

{
    "dependencies": {
        "config": "file:./config"
    }
}

(等價(jià)于第 2 步)直接在應(yīng)用目錄執(zhí)行 npm install file:./config

此時(shí),查看 node_modules 目錄我們會(huì)發(fā)現(xiàn)多出來(lái)一個(gè)名為 config,指向上層 config/ 文件夾的軟鏈接。這是因?yàn)?npm 識(shí)別 file: 協(xié)議的url,得知這個(gè)包需要直接從文件系統(tǒng)中獲取,會(huì)自動(dòng)創(chuàng)建軟鏈接到 node_modules 中,完成“安裝”過(guò)程。

相比手動(dòng)軟鏈,我們既不需要關(guān)心 windows 和 linux 命令差異,又可以顯式地將依賴(lài)信息固化到 dependencies 字段中,開(kāi)發(fā)團(tuán)隊(duì)其他成員可以執(zhí)行 npm install 后直接使用。

場(chǎng)景2: 私有 git 共享 package

有些時(shí)候,我們一個(gè)團(tuán)隊(duì)內(nèi)會(huì)有一些代碼/公用庫(kù)需要在團(tuán)隊(duì)內(nèi)不同項(xiàng)目間共享,但可能由于包含了敏感內(nèi)容,或者代碼太爛拿不出手等原因,不方便發(fā)布到源。

這種情況下,我們可以簡(jiǎn)單地將被依賴(lài)的包托管在私有的 git 倉(cāng)庫(kù)中,然后將該 git url 保存到 dependencies 中. npm 會(huì)直接調(diào)用系統(tǒng)的 git 命令從 git 倉(cāng)庫(kù)拉取包的內(nèi)容到 node_modules 中。

npm 支持的 git url 格式:

://[[:]@][:][:][/][# | #semver:]

git 路徑后可以使用 # 指定特定的 git branch/commit/tag, 也可以 #semver: 指定特定的 semver range.

例如:

git+ssh://git@github.com:npm/npm.git#v1.0.27
git+ssh://git@github.com:npm/npm#semver:^5.0
git+https://isaacs@github.com/npm/npm.git
git://github.com/npm/npm.git#v1.0.27

場(chǎng)景3: 開(kāi)源 package 問(wèn)題修復(fù)

使用某個(gè) npm 包時(shí)發(fā)現(xiàn)它有某個(gè)嚴(yán)重bug,但也許最初作者已不再維護(hù)代碼了,也許我們工作緊急,沒(méi)有足夠的時(shí)間提 issue 給作者再慢慢等作者發(fā)布新的修復(fù)版本到 npm 源。

此時(shí)我們可以手動(dòng)進(jìn)入 node_modules 目錄下修改相應(yīng)的包內(nèi)容,也許修改了一行代碼就修復(fù)了問(wèn)題。但是這種做法非常不明智!

首先 node_modules 本身不應(yīng)該放進(jìn)版本控制系統(tǒng),對(duì) node_modules 文件夾中內(nèi)容的修改不會(huì)被記錄進(jìn) git 提交記錄;其次,就算我們非要反模式,把 node_modules 放進(jìn)版本控制中,你的修改內(nèi)容也很容易在下次 team 中某位成員執(zhí)行 npm installnpm update 時(shí)被覆蓋,而這樣的一次提交很可能包含了幾十幾百個(gè)包的更新,你自己所做的修改很容易就被淹沒(méi)在龐大的 diff 文件列表中了。

方案:

最好的辦法應(yīng)當(dāng)是 fork 原作者的 git 庫(kù),在自己所屬的 repo 下修復(fù)問(wèn)題后,將 dependencies 中相應(yīng)的依賴(lài)項(xiàng)更改為自己修復(fù)后版本的 git url 即可解決問(wèn)題。(Fork 代碼庫(kù)后,也便于向原作者提交 PR 修復(fù)問(wèn)題。上游代碼庫(kù)修復(fù)問(wèn)題后,再次更新我們的依賴(lài)配置也不遲。)

3. npm install 如何工作 —— node_modules 目錄結(jié)構(gòu)

npm install 執(zhí)行完畢后,我們可以在 node_modules 中看到所有依賴(lài)的包。雖然使用者無(wú)需關(guān)注這個(gè)目錄里的文件夾結(jié)構(gòu)細(xì)節(jié),只管在業(yè)務(wù)代碼中引用依賴(lài)包即可,但了解 node_modules 的內(nèi)容可以幫我們更好理解 npm 如何工作,了解從 npm 2 到 npm 5 有哪些變化和改進(jìn)。

為簡(jiǎn)單起見(jiàn),我們假設(shè)應(yīng)用目錄為 app, 用兩個(gè)流行的包 webpack, nconf 作為依賴(lài)包做示例說(shuō)明。并且為了正常安裝,使用了“上古” npm 2 時(shí)期的版本 webpack@1.15.0, nconf@0.8.5.

3.1 npm 2

npm 2 在安裝依賴(lài)包時(shí),采用簡(jiǎn)單的遞歸安裝方法。執(zhí)行 npm install 后,npm 2 依次遞歸安裝 webpacknconf 兩個(gè)包到 node_modules 中。執(zhí)行完畢后,我們會(huì)看到 ./node_modules 這層目錄只含有這兩個(gè)子目錄。

node_modules/
├── nconf/
└── webpack/

進(jìn)入更深一層 nconf 或 webpack 目錄,將看到這兩個(gè)包各自的 node_modules 中,已經(jīng)由 npm 遞歸地安裝好自身的依賴(lài)包。包括 ./node_modules/webpack/node_modules/webpack-core , ./node_modules/conf/node_modules/async 等等。而每一個(gè)包都有自己的依賴(lài)包,每個(gè)包自己的依賴(lài)都安裝在了自己的 node_modules 中。依賴(lài)關(guān)系層層遞進(jìn),構(gòu)成了一整個(gè)依賴(lài)樹(shù),這個(gè)依賴(lài)樹(shù)與文件系統(tǒng)中的文件結(jié)構(gòu)樹(shù)剛好層層對(duì)應(yīng)。

最方便的查看依賴(lài)樹(shù)的方式是直接在 app 目錄下執(zhí)行 npm ls 命令。

app@0.1.0
├─┬ nconf@0.8.5
│ ├── async@1.5.2
│ ├── ini@1.3.5
│ ├── secure-keys@1.0.0
│ └── yargs@3.32.0
└─┬ webpack@1.15.0
  ├── acorn@3.3.0
  ├── async@1.5.2
  ├── clone@1.0.3
  ├── ...
  ├── optimist@0.6.1
  ├── supports-color@3.2.3
  ├── tapable@0.1.10
  ├── uglify-js@2.7.5
  ├── watchpack@0.2.9
  └─┬ webpack-core@0.6.9
    ├── source-list-map@0.1.8
    └── source-map@0.4.4

這樣的目錄結(jié)構(gòu)優(yōu)點(diǎn)在于層級(jí)結(jié)構(gòu)明顯,便于進(jìn)行傻瓜式的管理:

例如新裝一個(gè)依賴(lài)包,可以立即在第一層 node_modules 中看到子目錄

在已知所需包名和版本號(hào)時(shí),甚至可以從別的文件夾手動(dòng)拷貝需要的包到 node_modules 文件夾中,再手動(dòng)修改 package.json 中的依賴(lài)配置

要?jiǎng)h除這個(gè)包,也可以簡(jiǎn)單地手動(dòng)刪除這個(gè)包的子目錄,并刪除 package.json 文件中相應(yīng)的一行即可

實(shí)際上,很多人在 npm 2 時(shí)代也的確都這么實(shí)踐過(guò),的確也都可以安裝和刪除成功,并不會(huì)導(dǎo)致什么差錯(cuò)。

但這樣的文件結(jié)構(gòu)也有很明顯的問(wèn)題:

對(duì)復(fù)雜的工程, node_modules 內(nèi)目錄結(jié)構(gòu)可能會(huì)太深,導(dǎo)致深層的文件路徑過(guò)長(zhǎng)而觸發(fā) windows 文件系統(tǒng)中,文件路徑不能超過(guò) 260 個(gè)字符長(zhǎng)的錯(cuò)誤

部分被多個(gè)包所依賴(lài)的包,很可能在應(yīng)用 node_modules 目錄中的很多地方被重復(fù)安裝。隨著工程規(guī)模越來(lái)越大,依賴(lài)樹(shù)越來(lái)越復(fù)雜,這樣的包情況會(huì)越來(lái)越多,造成大量的冗余。

——在我們的示例中就有這個(gè)問(wèn)題,webpacknconf 都依賴(lài) async 這個(gè)包,所以在文件系統(tǒng)中,webpack 和 nconf 的 node_modules 子目錄中都安裝了相同的 async 包,并且是相同的版本。

+-------------------------------------------+
|                   app/                    |
+----------+------------------------+-------+
           |                        |
           |                        |
+----------v------+       +---------v-------+
|                 |       |                 |
|  webpack@1.15.0 |       |  nconf@0.8.5    |
|                 |       |                 |
+--------+--------+       +--------+--------+
         |                         |
   +-----v-----+             +-----v-----+
   |async@1.5.2|             |async@1.5.2|
   +-----------+             +-----------+
3.2 npm 3 - 扁平結(jié)構(gòu)

主要為了解決以上問(wèn)題,npm 3 的 node_modules 目錄改成了更加扁平狀的層級(jí)結(jié)構(gòu)。文件系統(tǒng)中 webpack, nconf, async 的層級(jí)關(guān)系變成了平級(jí)關(guān)系,處于同一級(jí)目錄中。

         +-------------------------------------------+
         |                   app/                    |
         +-+---------------------------------------+-+
           |                                       |
           |                                       |
+----------v------+    +-------------+   +---------v-------+
|                 |    |             |   |                 |
|  webpack@1.15.0 |    | async@1.5.2 |   |  nconf@0.8.5    |
|                 |    |             |   |                 |
+-----------------+    +-------------+   +-----------------+

雖然這樣一來(lái) webpack/node_modules 和 nconf/node_modules 中都不再有 async 文件夾,但得益于 node 的模塊加載機(jī)制,他們都可以在上一級(jí) node_modules 目錄中找到 async 庫(kù)。所以 webpack 和 nconf 的庫(kù)代碼中 require("async") 語(yǔ)句的執(zhí)行都不會(huì)有任何問(wèn)題。

這只是最簡(jiǎn)單的例子,實(shí)際的工程項(xiàng)目中,依賴(lài)樹(shù)不可避免地會(huì)有很多層級(jí),很多依賴(lài)包,其中會(huì)有很多同名但版本不同的包存在于不同的依賴(lài)層級(jí),對(duì)這些復(fù)雜的情況, npm 3 都會(huì)在安裝時(shí)遍歷整個(gè)依賴(lài)樹(shù),計(jì)算出最合理的文件夾安裝方式,使得所有被重復(fù)依賴(lài)的包都可以去重安裝。

npm 文檔提供了更直觀的例子解釋這種情況:

假如 package{dep} 寫(xiě)法代表包和包的依賴(lài),那么  A{B,C}, B{C}, C{D} 的依賴(lài)結(jié)構(gòu)在安裝之后的 node_modules 是這樣的結(jié)構(gòu):
A
+-- B
+-- C
+-- D

這里之所以 D 也安裝到了與 B C 同一級(jí)目錄,是因?yàn)?npm 會(huì)默認(rèn)會(huì)在無(wú)沖突的前提下,盡可能將包安裝到較高的層級(jí)。

如果是 A{B,C}, B{C,D@1}, C{D@2} 的依賴(lài)關(guān)系,得到的安裝后結(jié)構(gòu)是:
A
+-- B
+-- C
   `-- D@2
+-- D@1

這里是因?yàn)椋瑢?duì)于 npm 來(lái)說(shuō)同名但不同版本的包是兩個(gè)獨(dú)立的包,而同層不能有兩個(gè)同名子目錄,所以其中的 D@2 放到了 C 的子目錄而另一個(gè) D@1 被放到了再上一層目錄。

很明顯在 npm 3 之后 npm 的依賴(lài)樹(shù)結(jié)構(gòu)不再與文件夾層級(jí)一一對(duì)應(yīng)了。想要查看 app 的直接依賴(lài)項(xiàng),要通過(guò) npm ls 命令指定 --depth 參數(shù)來(lái)查看:

npm ls --depth 1
PS: 與本地依賴(lài)包不同,如果我們通過(guò) npm install --global 全局安裝包到全局目錄時(shí),得到的目錄依然是“傳統(tǒng)的”目錄結(jié)構(gòu)。而如果使用 npm 3 想要得到“傳統(tǒng)”形式的本地 node_modules 目錄,使用 npm install --global-style 命令即可。
3.3 npm 5 - package-lock 文件

npm 5 發(fā)布于 2017 年也是目前最新的 npm 版本,這一版本依然沿用 npm 3 之后扁平化的依賴(lài)包安裝方式,此外最大的變化是增加了 package-lock.json 文件。

package-lock.json 的作用是鎖定依賴(lài)安裝結(jié)構(gòu),如果查看這個(gè) json 的結(jié)構(gòu),會(huì)發(fā)現(xiàn)與 node_modules 目錄的文件層級(jí)結(jié)構(gòu)是一一對(duì)應(yīng)的。

以依賴(lài)關(guān)系為: app{webpack} 的 "app" 項(xiàng)目為例, 其 package-lock 文件包含了這樣的片段。

{
    "name":  "app",
    "version":  "0.1.0",
    "lockfileVersion":  1,
    "requires":  true,
    "dependencies": {
        // ... 其他依賴(lài)包
        "webpack": {
            "version": "1.8.11",
            "resolved": "https://registry.npmjs.org/webpack/-/webpack-1.8.11.tgz",
            "integrity": "sha1-Yu0hnstBy/qcKuanu6laSYtgkcI=",
            "requires": {
                "async": "0.9.2",
                "clone": "0.1.19",
                "enhanced-resolve": "0.8.6",
                "esprima": "1.2.5",
                "interpret": "0.5.2",
                "memory-fs": "0.2.0",
                "mkdirp": "0.5.1",
                "node-libs-browser": "0.4.3",
                "optimist": "0.6.1",
                "supports-color": "1.3.1",
                "tapable": "0.1.10",
                "uglify-js": "2.4.24",
                "watchpack": "0.2.9",
                "webpack-core": "0.6.9"
            }
        },
        "webpack-core": {
            "version": "0.6.9",
            "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz",
            "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=",
            "requires": {
                "source-list-map": "0.1.8",
                "source-map": "0.4.4"
            },
            "dependencies": {
                "source-map": {
                    "version": "0.4.4",
                    "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
                    "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
                    "requires": {
                        "amdefine": "1.0.1"
                    }
                }
            }
        },
        //... 其他依賴(lài)包
    }
}

看懂 package-lock 文件并不難,其結(jié)構(gòu)是同樣類(lèi)型的幾個(gè)字段嵌套起來(lái)的,主要是 version, resolved, integrity, requires, dependencies 這幾個(gè)字段而已。

version, resolved, integrity 用來(lái)記錄包的準(zhǔn)確版本號(hào)、內(nèi)容hash、安裝源的,決定了要安裝的包的準(zhǔn)確“身份”信息

假設(shè)蓋住其他字段,只關(guān)注文件中的 dependencies: {} 我們會(huì)發(fā)現(xiàn),整個(gè)文件的 JSON 配置里的 dependencies 層次結(jié)構(gòu)與文件系統(tǒng)中 node_modules 的文件夾層次結(jié)構(gòu)是完全對(duì)照的

只關(guān)注 requires: {} 字段又會(huì)發(fā)現(xiàn),除最外層的 requires 屬性為 true 以外, 其他層的 requires 屬性都對(duì)應(yīng)著這個(gè)包的 package.json 里記錄的自己的依賴(lài)項(xiàng)

因?yàn)檫@個(gè)文件記錄了 node_modules 里所有包的結(jié)構(gòu)、層級(jí)和版本號(hào)甚至安裝源,它也就事實(shí)上提供了 “保存” node_modules 狀態(tài)的能力。只要有這樣一個(gè) lock 文件,不管在那一臺(tái)機(jī)器上執(zhí)行 npm install 都會(huì)得到完全相同的 node_modules 結(jié)果。

這就是 package-lock 文件致力于優(yōu)化的場(chǎng)景:在從前僅僅用 package.json 記錄依賴(lài),由于 semver range 的機(jī)制;一個(gè)月前由 A 生成的 package.json 文件,B 在一個(gè)月后根據(jù)它執(zhí)行 npm install 所得到的 node_modules 結(jié)果很可能許多包都存在不同的差異,雖然 semver 機(jī)制的限制使得同一份 package.json 不會(huì)得到大版本不同的依賴(lài)包,但同一份代碼在不同環(huán)境安裝出不同的依賴(lài)包,依然是可能導(dǎo)致意外的潛在因素。

相同作用的文件在 npm 5 之前就有,稱(chēng)為 npm shrinkwrap 文件,二者作用完全相同,不同的是后者需要手動(dòng)生成,而 npm 5 默認(rèn)會(huì)在執(zhí)行 npm install 后就生成 package-lock 文件,并且建議你提交到 git/svn 代碼庫(kù)中。

package-lock.json 文件在最初 npm 5.0 默認(rèn)引入時(shí)也引起了相當(dāng)大的爭(zhēng)議。在 npm 5.0 中,如果已有 package-lock 文件存在,若手動(dòng)在 package.json 文件新增一條依賴(lài),再執(zhí)行 npm install, 新增的依賴(lài)并不會(huì)被安裝到 node_modules 中, package-lock.json 也不會(huì)做相應(yīng)的更新。這樣的表現(xiàn)與使用者的自然期望表現(xiàn)不符。在 npm 5.1 的首個(gè) Release 版本中這個(gè)問(wèn)題得以修復(fù)。這個(gè)事情告訴我們,要升級(jí),不要使用 5.0。

——但依然有反對(duì)的聲音認(rèn)為 package-lock 太復(fù)雜,對(duì)此 npm 也提供了禁用配置:

npm config set package-lock false
4. 依賴(lài)包版本管理

依賴(lài)包安裝完并不意味著就萬(wàn)事大吉了,版本的維護(hù)和更新也很重要。這一章介紹依賴(lài)包升級(jí)管理相關(guān)知識(shí),太長(zhǎng)不看版本請(qǐng)直接跳到 4.3 最佳實(shí)踐

4.1 semver

npm 依賴(lài)管理的一個(gè)重要特性是采用了語(yǔ)義化版本 (semver) 規(guī)范,作為依賴(lài)版本管理方案。

semver 約定一個(gè)包的版本號(hào)必須包含3個(gè)數(shù)字,格式必須為 MAJOR.MINOR.PATCH, 意為 主版本號(hào).小版本號(hào).修訂版本號(hào).

MAJOR 對(duì)應(yīng)大的版本號(hào)迭代,做了不兼容舊版的修改時(shí)要更新 MAJOR 版本號(hào)

MINOR 對(duì)應(yīng)小版本迭代,發(fā)生兼容舊版API的修改或功能更新時(shí),更新MINOR版本號(hào)

PATCH 對(duì)應(yīng)修訂版本號(hào),一般針對(duì)修復(fù) BUG 的版本號(hào)

對(duì)于包作者(發(fā)布者),npm 要求在 publish 之前,必須更新版本號(hào)。npm 提供了 npm version 工具,執(zhí)行 npm version major|minor|patch 可以簡(jiǎn)單地將版本號(hào)中相應(yīng)的數(shù)字加1.

如果包是一個(gè) git 倉(cāng)庫(kù),npm version 還會(huì)自動(dòng)創(chuàng)建一條注釋為更新后版本號(hào)的 git commit 和名為該版本號(hào)的 tag

對(duì)于包的引用者來(lái)說(shuō),我們需要在 dependencies 中使用 semver 約定的 semver range 指定所需依賴(lài)包的版本號(hào)或版本范圍。npm 提供了網(wǎng)站 https://semver.npmjs.com 可方便地計(jì)算所輸入的表達(dá)式的匹配范圍。常用的規(guī)則示例如下表:

range 含義
^2.2.1 指定的 MAJOR 版本號(hào)下, 所有更新的版本 匹配 2.2.3, 2.3.0; 不匹配 1.0.3, 3.0.1
~2.2.1 指定 MAJOR.MINOR 版本號(hào)下,所有更新的版本 匹配 2.2.3, 2.2.9 ; 不匹配 2.3.0, 2.4.5
>=2.1 版本號(hào)大于或等于 2.1.0 匹配 2.1.2, 3.1
<=2.2 版本號(hào)小于或等于 2.2 匹配 1.0.0, 2.2.1, 2.2.11
1.0.0 - 2.0.0 版本號(hào)從 1.0.0 (含) 到 2.0.0 (含) 匹配 1.0.0, 1.3.4, 2.0.0

任意兩條規(guī)則,通過(guò) || 連接起來(lái),則表示兩條規(guī)則的并集:

^2 >=2.3.1 || ^3 >3.2 可以匹配:

* `2.3.1`, `2,8.1`, `3.3.1`
* 但不匹配 `1.0.0`, `2.2.0`, `3.1.0`, `4.0.0`

PS: 除了這幾種,還有如下更直觀的表示版本號(hào)范圍的寫(xiě)法:

*x 匹配所有主版本

11.x 匹配 主版本號(hào)為 1 的所有版本

1.21.2.x 匹配 版本號(hào)為 1.2 開(kāi)頭的所有版本

PPS: 在常規(guī)僅包含數(shù)字的版本號(hào)之外,semver 還允許在 MAJOR.MINOR.PATCH 后追加 - 后跟點(diǎn)號(hào)分隔的標(biāo)簽,作為預(yù)發(fā)布版本標(biāo)簽 - Prerelese Tags,通常被視為不穩(wěn)定、不建議生產(chǎn)使用的版本。例如:

1.0.0-alpha

1.0.0-beta.1

1.0.0-rc.3

上表中我們最常見(jiàn)的是 ^1.8.11 這種格式的 range, 因?yàn)槲覀冊(cè)谑褂?npm install 安裝包時(shí),npm 默認(rèn)安裝當(dāng)前最新版本,例如 1.8.11, 然后在所安裝的版本號(hào)前加^號(hào), 將 ^1.8.11 寫(xiě)入 package.json 依賴(lài)配置,意味著可以匹配 1.8.11 以上,2.0.0 以下的所有版本。

4.2 依賴(lài)版本升級(jí)

問(wèn)題來(lái)了,在安裝完一個(gè)依賴(lài)包之后有新版本發(fā)布了,如何使用 npm 進(jìn)行版本升級(jí)呢?——答案是簡(jiǎn)單的 npm installnpm update,但在不同的 npm 版本,不同的 package.json, package-lock.json 文件,安裝/升級(jí)的表現(xiàn)也不同。

我們不妨還以 webpack 舉例,做如下的前提假設(shè):

我們的工程項(xiàng)目 app 依賴(lài) webpack

項(xiàng)目最初初始化時(shí),安裝了當(dāng)時(shí)最新的包 webpack@1.8.0,并且 package.json 中的依賴(lài)配置為: "webpack": "^1.8.0"

當(dāng)前(2018年3月) webpack 最新版本為 4.2.0, webpack 1.x 最新子版本為 1.15.0

如果我們使用的是 npm 3, 并且項(xiàng)目不含 package-lock.json, 那么根據(jù) node_modules 是否為空,執(zhí)行 install/update 的結(jié)果如下 (node 6.13.1, npm 3.10.10 環(huán)境下試驗(yàn)):

# package.json (BEFORE) node_modules (BEFORE) command (npm 3) package.json (AFTER) node_modules (AFTER)
a) webpack: ^1.8.0 webpack@1.8.0 install webpack: ^1.8.0 webpack@1.8.0
b) webpack: ^1.8.0 install webpack: ^1.8.0 webpack@1.15.0
c) webpack: ^1.8.0 webpack@1.8.0 update webpack: ^1.8.0 webpack@1.15.0
d) webpack: ^1.8.0 update webpack: ^1.8.0 webpack@1.15.0

根據(jù)這個(gè)表我們可以對(duì) npm 3 得出以下結(jié)論:

如果本地 node_modules 已安裝,再次執(zhí)行 install 不會(huì)更新包版本, 執(zhí)行 update 才會(huì)更新; 而如果本地 node_modules 為空時(shí),執(zhí)行 install/update 都會(huì)直接安裝更新包;

npm update 總是會(huì)把包更新到符合 package.json 中指定的 semver 的最新版本號(hào)——本例中符合 ^1.8.0 的最新版本為 1.15.0

一旦給定 package.json, 無(wú)論后面執(zhí)行 npm install 還是 update, package.json 中的 webpack 版本一直頑固地保持 一開(kāi)始的 ^1.8.0 巋然不動(dòng)

這里不合理的地方在于,如果最開(kāi)始團(tuán)隊(duì)中第一個(gè)人安裝了 webpack@1.8.0, 而新加入項(xiàng)目的成員, checkout 工程代碼后執(zhí)行 npm install 會(huì)安裝得到不太一樣的 1.15.0 版本。雖然 semver 約定了小版本號(hào)應(yīng)當(dāng)保持向下兼容(相同大版本號(hào)下的小版本號(hào))兼容,但萬(wàn)一有不熟悉不遵循此約定的包發(fā)布者,發(fā)布了不兼容的包,此時(shí)就可能出現(xiàn)因依賴(lài)環(huán)境不同導(dǎo)致的 bug。

下面由 npm 5 帶著 package-lock.json 閃亮登場(chǎng),執(zhí)行 install/update 的效果是這樣的 (node 9.8.0, npm 5.7.1 環(huán)境下試驗(yàn)):

下表為表述簡(jiǎn)單,省略了包名 webpack, install 簡(jiǎn)寫(xiě) i, update 簡(jiǎn)寫(xiě)為 up
# package.json (BEFORE) node_modules (BEFORE) package-lock (BEFORE) command package.json (AFTER) node_modules (AFTER)
a) ^1.8.0 @1.8.0 @1.8.0 i ^1.8.0 @1.8.0
b) ^1.8.0 @1.8.0 i ^1.8.0 @1.8.0
c) ^1.8.0 @1.8.0 @1.8.0 up ^1.15.0 @1.15.0
d) ^1.8.0 @1.8.0 up ^1.8.0 @1.15.0
e) ^1.15.0 @1.8.0 (舊) @1.15.0 i ^1.15.0 @1.15.0
f) ^1.15.0 @1.8.0 (舊) @1.15.0 up ^1.15.0 @1.15.0

與 npm 3 相比,在安裝和更新依賴(lài)版本上主要的區(qū)別為:

無(wú)論何時(shí)執(zhí)行 install, npm 都會(huì)優(yōu)先按照 package-lock 中指定的版本來(lái)安裝 webpack; 避免了 npm 3 表中情形 b) 的狀況;

無(wú)論何時(shí)完成安裝/更新, package-lock 文件總會(huì)跟著 node_modules 更新 —— (因此可以視 package-lock 文件為 node_modules 的 JSON 表述)

已安裝 node_modules 后若執(zhí)行 npm update,package.json 中的版本號(hào)也會(huì)隨之更改為 ^1.15.0

由此可見(jiàn) npm 5.1 使得 package.json 和 package-lock.json 中所保存的版本號(hào)更加統(tǒng)一,解決了 npm 之前的各種問(wèn)題。只要遵循好的實(shí)踐習(xí)慣,團(tuán)隊(duì)成員可以很方便地維護(hù)一套應(yīng)用代碼和 node_modules 依賴(lài)都一致的環(huán)境。

皆大歡喜。

4.3 最佳實(shí)踐

總結(jié)起來(lái),在 2018 年 (node 9.8.0, npm 5.7.1) 時(shí)代,我認(rèn)為的依賴(lài)版本管理應(yīng)當(dāng)是:

使用 npm: >=5.1 版本, 保持 package-lock.json 文件默認(rèn)開(kāi)啟配置

初始化:第一作者初始化項(xiàng)目時(shí)使用 npm install 安裝依賴(lài)包, 默認(rèn)保存 ^X.Y.Z 依賴(lài) range 到 package.json中; 提交 package.json, package-lock.json, 不要提交 node_modules 目錄

初始化:項(xiàng)目成員首次 checkout/clone 項(xiàng)目代碼后,執(zhí)行一次 npm install 安裝依賴(lài)包

不要手動(dòng)修改 package-lock.json

升級(jí)依賴(lài)包:

升級(jí)小版本: 本地執(zhí)行 npm update 升級(jí)到新的小版本

升級(jí)大版本: 本地執(zhí)行 npm install @ 升級(jí)到新的大版本

也可手動(dòng)修改 package.json 中版本號(hào)為要升級(jí)的版本(大于現(xiàn)有版本號(hào))并指定所需的 semver, 然后執(zhí)行 npm install

本地驗(yàn)證升級(jí)后新版本無(wú)問(wèn)題后,提交新的 package.json, package-lock.json 文件

降級(jí)依賴(lài)包:

正確: npm install @ 驗(yàn)證無(wú)問(wèn)題后,提交 package.json 和 package-lock.json 文件

錯(cuò)誤: 手動(dòng)修改 package.json 中的版本號(hào)為更低版本的 semver, 這樣修改并不會(huì)生效,因?yàn)樵俅螆?zhí)行 npm install 依然會(huì)安裝 package-lock.json 中的鎖定版本

刪除依賴(lài)包:

Plan A: npm uninstall 并提交 package.jsonpackage-lock.json

Plan B: 把要卸載的包從 package.json 中 dependencies 字段刪除, 然后執(zhí)行 npm install 并提交 package.jsonpackage-lock.json

任何時(shí)候有人提交了 package.json, package-lock.json 更新后,團(tuán)隊(duì)其他成員應(yīng)在 svn update/git pull 拉取更新后執(zhí)行 npm install 腳本安裝更新后的依賴(lài)包

恭喜你終于可以跟 rm -rf node_modules && npm install 這波操作說(shuō)拜拜了(其實(shí)并不會(huì))

5. npm scripts 5.1 基本使用

npm scripts 是 npm 另一個(gè)很重要的特性。通過(guò)在 package.json 中 scripts 字段定義一個(gè)腳本,例如:

{
    "scripts": {
        "echo": "echo HELLO WORLD"
    }
}

我們就可以通過(guò) npm run echo 命令來(lái)執(zhí)行這段腳本,像在 shell 中執(zhí)行該命令 echo HELLO WORLD 一樣,看到終端輸出 HELLO WORLD.

—— npm scripts 的基本使用就是這么簡(jiǎn)單,它提供了一個(gè)簡(jiǎn)單的接口用來(lái)調(diào)用工程相關(guān)的腳本。關(guān)于更詳細(xì)的相關(guān)信息,可以參考阮一峰老師的文章 npm script 使用指南 (2016年10月).

簡(jiǎn)要總結(jié)阮老師文章內(nèi)容:

npm run 命令執(zhí)行時(shí),會(huì)把 ./node_modules/.bin/ 目錄添加到執(zhí)行環(huán)境的 PATH 變量中,因此如果某個(gè)命令行包未全局安裝,而只安裝在了當(dāng)前項(xiàng)目的 node_modules 中,通過(guò) npm run 一樣可以調(diào)用該命令。

執(zhí)行 npm 腳本時(shí)要傳入?yún)?shù),需要在命令后加 -- 標(biāo)明, 如 npm run test -- --grep="pattern" 可以將 --grep="pattern" 參數(shù)傳給 test 命令

npm 提供了 pre 和 post 兩種鉤子機(jī)制,可以定義某個(gè)腳本前后的執(zhí)行腳本

運(yùn)行時(shí)變量:在 npm run 的腳本執(zhí)行環(huán)境內(nèi),可以通過(guò)環(huán)境變量的方式獲取許多運(yùn)行時(shí)相關(guān)信息,以下都可以通過(guò) process.env 對(duì)象訪(fǎng)問(wèn)獲得:

npm_lifecycle_event - 正在運(yùn)行的腳本名稱(chēng)

npm_package_ - 獲取當(dāng)前包 package.json 中某個(gè)字段的配置值:如 npm_package_name 獲取包名

npm_package__ - package.json 中嵌套字段屬性:如 npm_pacakge_dependencies_webpack 可以獲取到 package.json 中的 dependencies.webpack 字段的值,即 webpack 的版本號(hào)

5.2 node_modules/.bin 目錄

上面所說(shuō)的 node_modules/.bin 目錄,保存了依賴(lài)目錄中所安裝的可供調(diào)用的命令行包。

何謂命令行包?例如 webpack 就屬于一個(gè)命令行包。如果我們?cè)诎惭b webpack 時(shí)添加 --global 參數(shù),就可以在終端直接輸入 webpack 進(jìn)行調(diào)用。但如果不加 --global 參數(shù),我們會(huì)在 node_modules/.bin 目錄里看到名為 webpack 的文件,如果在終端直接輸入 ./node_modules/.bin/webpack 命令,一樣可以執(zhí)行。

這是因?yàn)?webpackpackage.json 文件中定義了 bin 字段為:

{
    "bin": {
        "webpack": "./bin/webpack.js"
    }
}

bin 字段的配置格式為: : , 即 命令名: 可執(zhí)行文件. npm 執(zhí)行 install 時(shí),會(huì)分析每個(gè)依賴(lài)包的 package.json 中的 bin 字段,并將其包含的條目安裝到 ./node_modules/.bin 目錄中,文件名為 。而如果是全局模式安裝,則會(huì)在 npm 全局安裝路徑的 bin 目錄下創(chuàng)建指向 名為 的軟鏈。因此,./node_modules/.bin/webpack 文件在通過(guò)命令行調(diào)用時(shí),實(shí)際上就是在執(zhí)行 node ./node_modules/.bin/webpack.js 命令。

正如上一節(jié)所說(shuō),npm run 命令在執(zhí)行時(shí)會(huì)把 ./node_modules/.bin 加入到 PATH 中,使我們可直接調(diào)用所有提供了命令行調(diào)用接口的依賴(lài)包。所以這里就引出了一個(gè)最佳實(shí)踐:

將項(xiàng)目依賴(lài)的命令行工具安裝到項(xiàng)目依賴(lài)文件夾中,然后通過(guò) npm scripts 調(diào)用;而非全局安裝

舉例而言 webpack 作為前端工程標(biāo)配的構(gòu)建工具,雖然我們都習(xí)慣了全局安裝并直接使用命令行調(diào)用,但不同的項(xiàng)目依賴(lài)的 webpack 版本可能不同,相應(yīng)的 webpack.config.js 配置文件也可能只兼容了特定版本的 webpack. 如果我們僅全局安裝了最新的 webpack 4.x 并使用 webpack 命令調(diào)用,在一個(gè)依賴(lài) webpack 3.x 的工程中就會(huì)無(wú)法成功執(zhí)行構(gòu)建。

但如果這類(lèi)工具總是本地安裝,我們要調(diào)用一個(gè)命令,要手動(dòng)添加 ./node_modules/.bin 這個(gè)長(zhǎng)長(zhǎng)的前綴,未免也太麻煩了,我們 nodejs 開(kāi)發(fā)者都很懶的。于是 npm 從5.2 開(kāi)始自帶了一個(gè)新的工具 npx.

5.3 npx

npx 的使用很簡(jiǎn)單,就是執(zhí)行 npx 即可,這里的 默認(rèn)就是 ./node_modules 目錄中安裝的可執(zhí)行腳本名。例如上面本地安裝好的 webpack 包,我們可以直接使用 npx webpack 執(zhí)行即可。

除了這種最簡(jiǎn)單的場(chǎng)景, npm cli 團(tuán)隊(duì)開(kāi)發(fā)者 Kat Marchán 還在這篇文章中介紹了其他幾種 npx 的神奇用法: Introducing npx: an npm package runner, 國(guó)內(nèi)有位開(kāi)發(fā)者 robin.law 將原文翻譯為中文 npx是什么,為什么需要npx?.

有興趣的可以戳鏈接了解,懶得點(diǎn)鏈接的,看總結(jié):

場(chǎng)景a) 一鍵執(zhí)行遠(yuǎn)程 npm 源的二進(jìn)制包

除了在 package 中執(zhí)行 ./node_modules/.bin 中已安裝的命令, 還可以直接指定未安裝的二進(jìn)制包名執(zhí)行。例如我們?cè)谝粋€(gè)沒(méi)有 package.json 也沒(méi)有 node_modules 的目錄下,執(zhí)行:

npx cowsay hello

npx 將會(huì)從 npm 源下載 cowsay 這個(gè)包(但并不安裝)并執(zhí)行:

 _______ 
< hello >
 ------- 
           ^__^
           (oo)\_______
            (__)       )/
                ||----w |
                ||     ||

這種用途非常適合 1. 在本地簡(jiǎn)單測(cè)試或調(diào)試 npm 源上這些二進(jìn)制包的功能;2. 調(diào)用 create-react-app 或 yeoman 這類(lèi)往往每個(gè)項(xiàng)目只需要使用一次的腳手架工具

PS: 此處有彩蛋,執(zhí)行這條命令試試:

npx workin-hard
場(chǎng)景b) 一鍵執(zhí)行 GitHub Gist

還記得前面提到的 2.1 package定義 么,npm install 可以是包含了有效 package.json 的 git url.

剛好 GitHub Gist 也是 git 倉(cāng)庫(kù) 的一種,集合 npx 就可以方便地將簡(jiǎn)單的腳本共享給其他人,擁有該鏈接的人無(wú)需將腳本安裝到本地工作目錄即可執(zhí)行。將 package.json 和 需執(zhí)行的二進(jìn)制腳本上傳至 gist, 在運(yùn)行 npx 就可以方便地執(zhí)行該 gist 定義的命令。

原文作者 Kat Marchán 提供了這個(gè)示例 gist, 執(zhí)行:

npx https://gist.github.com/zkat/4bc19503fe9e9309e2bfaa2c58074d32

可得到一個(gè)來(lái)自 GitHubGist 的 hello world 問(wèn)候。

場(chǎng)景c) 使用不同版本 node 執(zhí)行命令

將 npx 與 Aria Stewart 創(chuàng)建的 node 包 (https://www.npmjs.com/package... 結(jié)合,可以實(shí)現(xiàn)在一行命令中使用指定版本的 node 執(zhí)行命令。

例如先后執(zhí)行:

npx node@4 -e "console.log(process.version)"
npx node@6 -e "console.log(process.version)"

將分別輸出 v4.8.7v6.13.0.

往常這種工作是由 nvm 這類(lèi) node 版本管理工具來(lái)做的,但 npx node@4 這種方式免去 nvm 手動(dòng)切換配置的步驟,更加簡(jiǎn)潔簡(jiǎn)單。

6. npm 配置 6.1 npm config

npm cli 提供了 npm config 命令進(jìn)行 npm 相關(guān)配置,通過(guò) npm config ls -l 可查看 npm 的所有配置,包括默認(rèn)配置。npm 文檔頁(yè)為每個(gè)配置項(xiàng)提供了詳細(xì)的說(shuō)明 https://docs.npmjs.com/misc/c... .

修改配置的命令為 npm config set , 我們使用相關(guān)的常見(jiàn)重要配置:

proxy, https-proxy: 指定 npm 使用的代理

registry 指定 npm 下載安裝包時(shí)的源,默認(rèn)為 https://registry.npmjs.org/ 可以指定為私有 Registry 源

package-lock 指定是否默認(rèn)生成 package-lock 文件,建議保持默認(rèn) true

save true/false 指定是否在 npm install 后保存包為 dependencies, npm 5 起默認(rèn)為 true

刪除指定的配置項(xiàng)命令為 npm config delete .

6.2 npmrc 文件

除了使用 CLI 的 npm config 命令顯示更改 npm 配置,還可以通過(guò) npmrc 文件直接修改配置。

這樣的 npmrc 文件優(yōu)先級(jí)由高到低包括:

工程內(nèi)配置文件: /path/to/my/project/.npmrc

用戶(hù)級(jí)配置文件: ~/.npmrc

全局配置文件: $PREFIX/etc/npmrc (即npm config get globalconfig 輸出的路徑)

npm內(nèi)置配置文件: /path/to/npm/npmrc

通過(guò)這個(gè)機(jī)制,我們可以方便地在工程跟目錄創(chuàng)建一個(gè) .npmrc 文件來(lái)共享需要在團(tuán)隊(duì)間共享的 npm 運(yùn)行相關(guān)配置。比如如果我們?cè)诠緝?nèi)網(wǎng)環(huán)境下需通過(guò)代理才可訪(fǎng)問(wèn) registry.npmjs.org 源,或需訪(fǎng)問(wèn)內(nèi)網(wǎng)的 registry, 就可以在工作項(xiàng)目下新增 .npmrc 文件并提交代碼庫(kù)。

proxy = http://proxy.example.com/
https-proxy = http://proxy.example.com/
registry = http://registry.example.com/

因?yàn)轫?xiàng)目級(jí) .npmrc 文件的作用域只在本項(xiàng)目下,所以在非本目錄下,這些配置并不生效。對(duì)于使用筆記本工作的開(kāi)發(fā)者,可以很好地隔離公司的工作項(xiàng)目、在家學(xué)習(xí)研究項(xiàng)目?jī)煞N不同的環(huán)境。

將這個(gè)功能與 ~/.npm-init.js 配置相結(jié)合,可以將特定配置的 .npmrc 跟 .gitignore, README 之類(lèi)文件一起做到 npm init 腳手架中,進(jìn)一步減少手動(dòng)配置。

6.3 node 版本約束

雖然一個(gè)項(xiàng)目的團(tuán)隊(duì)都共享了相同的代碼,但每個(gè)人的開(kāi)發(fā)機(jī)器可能安裝了不同的 node 版本,此外服務(wù)器端的也可能與本地開(kāi)發(fā)機(jī)不一致。

這又是一個(gè)可能帶來(lái)不一致性的因素 —— 但也不是很難解決,聲明式約束+腳本限制即可。

聲明:通過(guò) package.jsonengines 屬性聲明應(yīng)用運(yùn)行所需的版本運(yùn)行時(shí)要求。例如我們的項(xiàng)目中使用了 async, await 特性,查閱兼容性表格得知最低支持版本為 7.6.0,因此指定 engines 配置為:

{
    "engines": { "node": ">=7.6.0"}
}

強(qiáng)約束(可選):在 npm 中以上字段內(nèi)容僅作為建議字段使用,若要在私有項(xiàng)目中添加強(qiáng)約束,需要自己寫(xiě)腳本鉤子,讀取并解析 engines 字段的 semver range 并與運(yùn)行時(shí)環(huán)境做對(duì)比校驗(yàn)并適當(dāng)提醒。

7. 小結(jié) npm 最佳實(shí)踐

使用 npm-init 初始化新項(xiàng)目

統(tǒng)一項(xiàng)目配置: 需團(tuán)隊(duì)共享的 npm config 配置項(xiàng),固化到 .npmrc 文件中

統(tǒng)一運(yùn)行環(huán)境,統(tǒng)一 package.json,統(tǒng)一 package-lock 文件

合理使用多樣化的源安裝依賴(lài)包: npm install |

使用 npm: >=5.2 版本

使用 npm scripts 與 npx (npm: >=5.2) 腳本管理應(yīng)用相關(guān)腳本

8. 更多資料

參考

npm team 成員 Ashley Williams 在 2016 年 Node.js Live 上的 talk: You Don"t Know npm, 當(dāng)時(shí)還沒(méi)有 npm 5

YouTube 視頻鏈接: Node.js Live (Paris) - Ashley Williams, You Don"t Know npm

演講用的 slides: the ag_deck

這篇 2015 年的文章介紹了如何使用把本地模塊打包到 node_modules 依賴(lài)中: Build modular application with npm local modules

一篇很好的介紹 package-lock.json 的文章: Everything you wanted to know about package-lock.json

阮一峰 npm scripts 使用指南

Kat Marchán 介紹npx:

原文 Introducing npx: an npm package runner

中文 npx是什么,為什么需要npx?

文檔

npm 官方文檔, 無(wú)中文翻譯

package.json 文件

npm config 配置

npm semver 計(jì)算器

node_modules 目錄扁平化

yarn 中文文檔,雖然是 npm 競(jìng)爭(zhēng)者但兼容 package.json 和 node_modules 目錄,因此這兩部分一樣可參考:

package.json - 中文

依賴(lài)與版本 - 中文

延伸閱讀

sam boyer 《所以你想開(kāi)發(fā)一個(gè)包管理系統(tǒng)》,從無(wú)關(guān)特定語(yǔ)言的角度,介紹一個(gè)包管理系統(tǒng)的方面: So you want to write a package manager

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/93656.html

相關(guān)文章

  • [譯]2018年值得關(guān)注的10大JavaScript動(dòng)畫(huà)庫(kù)

    摘要:幸運(yùn)的是,供應(yīng)似乎與需求相匹配,并且有多種選擇。讓我們來(lái)看看年值得關(guān)注的十大動(dòng)畫(huà)庫(kù)。八年了,仍然是一個(gè)強(qiáng)大的動(dòng)畫(huà)工具。接下來(lái)在這個(gè)令人驚嘆的動(dòng)畫(huà)庫(kù)列表上的就是了。,通常被稱(chēng)為動(dòng)畫(huà)平臺(tái),我們忽略它在列表中的排名,它是列表中最受歡迎的庫(kù)之一。 原文鏈接原譯文鏈接 現(xiàn)代網(wǎng)站的客戶(hù)端依靠高質(zhì)量的動(dòng)畫(huà),這就使得JavaScript動(dòng)畫(huà)庫(kù)的需求不斷增加。幸運(yùn)的是,供應(yīng)似乎與需求相匹配,并且有多種選...

    Me_Kun 評(píng)論0 收藏0
  • 【總結(jié)】我們2018年的關(guān)鍵詞-堅(jiān)持學(xué)習(xí)

    摘要:因?yàn)樯婕皹I(yè)務(wù)敏感話(huà)題,本文只記錄我們學(xué)習(xí)的歷程。我由衷的感謝團(tuán)隊(duì)的小伙伴們,感謝你們的堅(jiān)韌不拔,感謝你們的持續(xù)成長(zhǎng)。這個(gè)變化只是在每天的堅(jiān)持和刻意練習(xí)中發(fā)生的,是那么的神奇。 因?yàn)樯婕皹I(yè)務(wù)敏感話(huà)題,本文只記錄我們學(xué)習(xí)的歷程。 關(guān)于堅(jiān)持 ??從2016年起,我們團(tuán)隊(duì)堅(jiān)持每天早晨8:50-10:30的100分鐘早晨討論,到現(xiàn)在已經(jīng)兩年了,期間沒(méi)有中斷過(guò)。我由衷的感謝團(tuán)隊(duì)的小伙伴們,感謝你們...

    Imfan 評(píng)論0 收藏0
  • 【總結(jié)】我們2018年的關(guān)鍵詞-堅(jiān)持學(xué)習(xí)

    摘要:因?yàn)樯婕皹I(yè)務(wù)敏感話(huà)題,本文只記錄我們學(xué)習(xí)的歷程。我由衷的感謝團(tuán)隊(duì)的小伙伴們,感謝你們的堅(jiān)韌不拔,感謝你們的持續(xù)成長(zhǎng)。這個(gè)變化只是在每天的堅(jiān)持和刻意練習(xí)中發(fā)生的,是那么的神奇。 因?yàn)樯婕皹I(yè)務(wù)敏感話(huà)題,本文只記錄我們學(xué)習(xí)的歷程。 關(guān)于堅(jiān)持 ??從2016年起,我們團(tuán)隊(duì)堅(jiān)持每天早晨8:50-10:30的100分鐘早晨討論,到現(xiàn)在已經(jīng)兩年了,期間沒(méi)有中斷過(guò)。我由衷的感謝團(tuán)隊(duì)的小伙伴們,感謝你們...

    xingpingz 評(píng)論0 收藏0
  • 掘金 AMA:聽(tīng)《Android進(jìn)階解密》作者--劉望舒聊 Android 開(kāi)發(fā)、進(jìn)階那些事

    摘要:第二十二期掘金團(tuán)隊(duì)請(qǐng)來(lái)了進(jìn)階解密作者劉望舒做了為期三天的活動(dòng)活動(dòng)已結(jié)束。我們?cè)诖司x了一些來(lái)自用戶(hù)的提問(wèn)及劉望舒的回答。提醒本期分布式微服務(wù)主題的正在進(jìn)行,歡迎前去提問(wèn),傳送門(mén)關(guān)于劉望舒進(jìn)階之光進(jìn)階解密的作者,安卓巴士等技術(shù)大會(huì)特邀講師。第二十二期 AMA 掘金團(tuán)隊(duì)請(qǐng)來(lái)了《Android進(jìn)階解密》作者-- 劉望舒做了為期三天的 Ask Me Anything (AMA) 活動(dòng)(活動(dòng)已結(jié)束)。...

    wenyiweb 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<