透徹演示 dependencies 和 devDependencies 區(qū)別

官方說明

參考: Specifying dependencies and devDependencies in a package.json file

通過 package.json 文件中的 dependenciesdevDependencies 字段,可以指定項(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é)如下:

  1. fff-pkg-bdependencies 方式引入 fff-pkg-a@1.1.1,test 調(diào)用 bar 方法,最終會調(diào)用 fff-pkg-a@1.1.1 中的 foo 函數(shù)
  2. fff-pkg-bdevDependencies 方式引入 fff-pkg-a@1.1.1,因?yàn)?devDependencies 的依賴不能被遞歸安裝,會因?yàn)檎也坏?fff-pkg-a 庫導(dǎo)致 test 執(zhí)行報(bào)錯
  3. fff-pkg-bdevDependencies 方式引入 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ù)
  4. fff-pkg-bdependencies 方式引入 fff-pkg-a@1.1.1test 顯式安裝依賴 fff-pkg-a@1.1.2,test 調(diào)用 bar 方法,最終會調(diào)用 fff-pkg-a@1.1.1 中的 foo 函數(shù)

因此,dependencies 與 devDependencies 最主要的區(qū)別是,作為依賴包時(shí),能否被遞歸安裝依賴。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容