官方說明
參考: Specifying dependencies and devDependencies in a package.json file
通過 package.json 文件中的 dependencies 及 devDependencies 字段,可以指定項(xiàng)目依賴的包。執(zhí)行 npm install 后,npm 將下載 dependencies 和 devDependencies 所列出的包。
dependencies 和 devDependencies 的區(qū)別:
-
dependencies: 指定應(yīng)用在生產(chǎn)環(huán)境中所依賴的包; -
devDependencies: 指定應(yīng)用僅在本地開發(fā)和測試時(shí)所依賴的包。
上面的說明并不直觀,下面我們通過實(shí)驗(yàn)演示兩者的區(qū)別。
實(shí)戰(zhàn)
實(shí)戰(zhàn)概述: 我們創(chuàng)建兩個(gè) npm 包 fff-pkg-a 和 fff-pkg-b,其中 fff-pkg-b 依賴于 fff-pkg-a,分別以 dependencies 和 devDependencies 兩種方式指定依賴。之后我們再創(chuàng)建一個(gè)測試項(xiàng)目 test,查看不同依賴方式的效果。
創(chuàng)建 fff-pkg-a 包
首先,我們創(chuàng)建文件夾 fff-pkg-a,進(jìn)入該文件夾并通過 pnpm init 命令進(jìn)行初始化,創(chuàng)建 index.js 文件。最終該文件夾下有 package.json 和 index.js 兩個(gè)文件。內(nèi)容分別為:
- package.json:
{
"name": "fff-pkg-a",
"version": "1.1.1",
"description": "測試包發(fā)布",
"main": "index.js",
"type": "module",
"scripts": {},
"keywords": [],
"author": "",
"license": "ISC"
}
注意: 要將 package.json 的 type 字段設(shè)為 module 類型,否則 index.js 不能使用 export 語法。
- index.js
export function foo() {
console.log("fff-pkg-a@1.1.1");
}
最后通過 npm publish 發(fā)布 fff-pkg-a 包,發(fā)布 npm 包參考 如何發(fā)布一個(gè)自己的 npm 包。
創(chuàng)建 fff-pkg-b 包
創(chuàng)建 fff-pkg-b 文件夾,通過 pnpm init 初始化創(chuàng)建 package.json 文件。
通過 pnpm add fff-pkg-a 以 dependencies 的方式安裝依賴,并創(chuàng)建 index.js,最終,package.json 和 index.js 的內(nèi)容分別為:
- package.json
{
"name": "fff-pkg-b",
"version": "1.2.1",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"fff-pkg-a": "1.1.1"
}
}
- index.js
import { foo } from "fff-pkg-a";
export function bar() {
foo();
console.log("fff-pkg-b 【dependencies 方式】 依賴 fff-pkg-a");
}
創(chuàng)建測試項(xiàng)目
創(chuàng)建文件夾 test,初始化項(xiàng)目,引入 fff-pkg-b 的依賴:
$ mkdir test
$ cd test
$ pnpm init
$ pnpm add fff-pkg-b@1.2.1
創(chuàng)建 index.mjs :
import { bar } from "fff-pkg-b";
bar();
執(zhí)行 node index.mjs:
$ node index.mjs
fff-pkg-a@1.1.1
fff-pkg-b 【dependencies 方式】 依賴 fff-pkg-a
我們正確地調(diào)用了 fff-pkg-b 中的 bar 方法,而 bar 方法也正確地調(diào)用了 fff-pkg-a 中的 foo 方法。
查看 node_modules 中的文件結(jié)構(gòu):
$ tree -a node_modules
node_modules
├── .modules.yaml
├── .pnpm
│ ├── fff-pkg-a@1.1.1
│ │ └── node_modules
│ │ └── fff-pkg-a
│ │ ├── index.js
│ │ └── package.json
│ ├── fff-pkg-b@1.2.1
│ │ └── node_modules
│ │ ├── fff-pkg-a -> ../../fff-pkg-a@1.1.1/node_modules/fff-pkg-a
│ │ └── fff-pkg-b
│ │ ├── index.js
│ │ └── package.json
│ ├── lock.yaml
│ └── node_modules
│ └── fff-pkg-a -> ../fff-pkg-a@1.1.1/node_modules/fff-pkg-a
└── fff-pkg-b -> .pnpm/fff-pkg-b@1.2.1/node_modules/fff-pkg-b
因?yàn)?test 項(xiàng)目顯式依賴 fff-pkg-b,因此在 node_modules 根目錄下有 fff-pkg-b 軟連接,指向 fff-pkg-b 文件夾。并且 .pnpm 目錄下有 fff-pkg-a@1.1.1,該文件夾被 fff-pkg-b@1.2.1 中的一個(gè)軟連接所引用,表明了 fff-pkg-b 依賴于 fff-pkg-a。
修改依賴方式為 devDependencies
修改 fff-pkg-b 中依賴方式及版本號,package.json 的內(nèi)容變?yōu)?
{
"name": "fff-pkg-b",
"version": "1.2.3",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"fff-pkg-a": "1.1.1"
}
}
并修改 index.js 的打印信息:
import { foo } from "fff-pkg-a";
export function bar() {
foo();
console.log("fff-pkg-b 【devDependencies 方式】 依賴 fff-pkg-a");
}
通過 npm publish 重新發(fā)布 fff-pkg-b。
刪除 test 項(xiàng)目中的 node_modules,并將 package.json 中對 fff-pkg-a 的依賴變?yōu)?1.2.3:
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {},
"dependencies": {
"fff-pkg-b": "1.2.3"
}
}
通過 pnpm i 重新安裝依賴后,執(zhí)行 node index.mjs 將報(bào)錯:
$ node index.mjs
node:internal/modules/esm/resolve:844
throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base), null);
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'fff-pkg-a' imported from test/node_modules/.pnpm/fff-pkg-b@1.2.3/node_modules/fff-pkg-b/index.js
at packageResolve (node:internal/modules/esm/resolve:844:9)
at moduleResolve (node:internal/modules/esm/resolve:901:20)
at defaultResolve (node:internal/modules/esm/resolve:1121:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:396:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:365:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:240:38)
at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:85:39)
at link (node:internal/modules/esm/module_job:84:36) {
code: 'ERR_MODULE_NOT_FOUND'
}
Node.js v20.10.0
報(bào)錯信息提示 fff-pkg-b 的依賴 fff-pkg-a 不能找到,可見,devDependencies 類型的依賴,不會被遞歸下載安裝,此時(shí)查看 node_modules 的文件樹:
$ tree -a node_modules
node_modules
├── .modules.yaml
├── .pnpm
│ ├── fff-pkg-b@1.2.3
│ │ └── node_modules
│ │ └── fff-pkg-b
│ │ ├── index.js
│ │ └── package.json
│ └── lock.yaml
└── fff-pkg-b -> .pnpm/fff-pkg-b@1.2.3/node_modules/fff-pkg-b
可見的確沒有下載 fff-pkg-a。如果此時(shí)我們在 test 中手動引入 fff-pkg-a 包會怎么樣呢?
手動引入缺失的依賴
為了區(qū)分,我們先修改并發(fā)布一個(gè)更高版本的 fff-pkg-a 庫。修改 fff-pkg-a 的 index.js:
export function foo() {
console.log("fff-pkg-a@1.1.2");
}
并將 fff-pkg-a 中 package.json 的 version 字段修改為 1.1.2,重新發(fā)布 fff-pkg-a。
在 test 項(xiàng)目中,通過 pnpm add fff-pkg-a@1.1.2 引入 fff-pkg-a。
此時(shí)執(zhí)行結(jié)果為:
$ node index.mjs
fff-pkg-a@1.1.2
fff-pkg-b 【devDependencies 方式】 依賴 fff-pkg-a
查詢正常執(zhí)行,node_modules 中的樹結(jié)果為:
$ tree -a node_modules
node_modules
├── .modules.yaml
├── .pnpm
│ ├── fff-pkg-a@1.1.2
│ │ └── node_modules
│ │ └── fff-pkg-a
│ │ ├── index.js
│ │ └── package.json
│ ├── fff-pkg-b@1.2.3
│ │ └── node_modules
│ │ └── fff-pkg-b
│ │ ├── index.js
│ │ └── package.json
│ └── lock.yaml
├── fff-pkg-a -> .pnpm/fff-pkg-a@1.1.2/node_modules/fff-pkg-a
└── fff-pkg-b -> .pnpm/fff-pkg-b@1.2.3/node_modules/fff-pkg-b
此時(shí),如果我們引入 fff-pkg-b@1.2.1 版本會怎么樣呢?即 test 自身引入的是 fff-pkg-a@1.1.2,fff-pkg-b@1.2.1 以 dependencies 引入的是 fff-pkg-a@1.1.1,此時(shí) test 項(xiàng)目中有多個(gè)版本的 fff-pkg-a 庫存在,那輸出的會是什么結(jié)果?
多個(gè) fff-pkg-a 版本并存
修改 test 中 packag-pkg-b 的版本為 1.2.1:
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {},
"dependencies": {
"fff-pkg-a": "1.1.2",
"fff-pkg-b": "1.2.1"
}
}
刪除 node_modules 后重新通過 pnpm i 安裝依賴。
執(zhí)行結(jié)果為:
$ node index.mjs
fff-pkg-a@1.1.1
fff-pkg-b 【dependencies 方式】 依賴 fff-pkg-a
可以看到 node_modules 的確存在多個(gè)版本的 fff-pkg-a 并存,并且 fff-pkg-b 使用的是通過其自身 package.json 指定的 fff-pkg-a 版本。
$ tree -a node_modules
node_modules
├── .modules.yaml
├── .pnpm
│ ├── fff-pkg-a@1.1.1
│ │ └── node_modules
│ │ └── fff-pkg-a
│ │ ├── index.js
│ │ └── package.json
│ ├── fff-pkg-a@1.1.2
│ │ └── node_modules
│ │ └── fff-pkg-a
│ │ ├── index.js
│ │ └── package.json
│ ├── fff-pkg-b@1.2.1
│ │ └── node_modules
│ │ ├── fff-pkg-a -> ../../fff-pkg-a@1.1.1/node_modules/fff-pkg-a
│ │ └── fff-pkg-b
│ │ ├── index.js
│ │ └── package.json
│ └── lock.yaml
├── fff-pkg-a -> .pnpm/fff-pkg-a@1.1.2/node_modules/fff-pkg-a
└── fff-pkg-b -> .pnpm/fff-pkg-b@1.2.1/node_modules/fff-pkg-b
總結(jié)
上述實(shí)驗(yàn)可以總結(jié)如下:
-
fff-pkg-b 以 dependencies 方式引入 fff-pkg-a@1.1.1,test 調(diào)用
bar方法,最終會調(diào)用 fff-pkg-a@1.1.1 中的foo函數(shù) - fff-pkg-b 以 devDependencies 方式引入 fff-pkg-a@1.1.1,因?yàn)?devDependencies 的依賴不能被遞歸安裝,會因?yàn)檎也坏?fff-pkg-a 庫導(dǎo)致 test 執(zhí)行報(bào)錯
-
fff-pkg-b 以 devDependencies 方式引入 fff-pkg-a@1.1.1,test 顯式安裝依賴 fff-pkg-a@1.1.2,test 調(diào)用
bar方法,最終會調(diào)用 fff-pkg-a@1.1.2 中的foo函數(shù) -
fff-pkg-b 以 dependencies 方式引入 fff-pkg-a@1.1.1,test 顯式安裝依賴 fff-pkg-a@1.1.2,test 調(diào)用
bar方法,最終會調(diào)用 fff-pkg-a@1.1.1 中的foo函數(shù)
因此,dependencies 與 devDependencies 最主要的區(qū)別是,作為依賴包時(shí),能否被遞歸安裝依賴。