你真的了解package.json嗎?
今天我們就來講講「package.json」。
還有很多同學(xué)說,f_cli[1]啥時候開源。這里簡單說一嘴,因為f_cli現(xiàn)在只適配了針對vite+react的模板,然后我想著想把vite+vue/webpack+react/vue/rsPack+vue/react最起碼再適配1-2個后,才有開源計劃。
1. 什么是package.json
package.json 是JS/TS項目的說明書和指導(dǎo)手冊
按照功能來分類,package.json具有如下的功能:(有些功能是可以相互配合使用的)
依賴管理
一個成熟的語言,不僅僅需要在語言層面功能完備,還體現(xiàn)在構(gòu)建大型項目時是否具有低成本的依賴管理能力。
現(xiàn)在有許多用于依賴管理的工具,比如 Java 的 Maven,Python 的 pip,JavaScript 的 npm/pnpm /yarn,以及 Rust 的 Cargo。
每個包管理器都需要一種方式來跟蹤在「當(dāng)前項目中應(yīng)該使用哪些版本的哪些軟件包」。通常,會創(chuàng)建一個文件,將這些「依賴關(guān)系映射到它們對應(yīng)的版本」上。比如,
- JS項目開發(fā)時,在根目錄上會存在package.json
- 在Rust項目開發(fā)時,根目錄下有Cargo.toml
我們今天的主角 -package.json的主要作用是「跟蹤項目中所需的所有依賴關(guān)系」。當(dāng)運行 JS 包管理器上的 install 命令時,它將安裝在 package.json 文件中提到的軟件包的相應(yīng)版本。
定義命令&執(zhí)行任務(wù)
這點大家都很熟,在現(xiàn)在前端開發(fā)中,尤其在SPA模式開發(fā)下,我們通常會使用打包工具Vite/Webpack/rsPack分別對開發(fā)模式和生產(chǎn)模式進行區(qū)分。要想實現(xiàn)上述操作。我們就需要在一個地方定義一些操作:執(zhí)行命令,并且能夠在通過npm cli命令行執(zhí)行這些「操作」。
而這就是package.json中scripts的用途。我們可以定義很多操作然后執(zhí)行諸如dev/build/prepare/postinstall等命令。
這個是利用npm的生命周期做一些資源整合的事情。這個我們會另寫一篇文章。這里也不再多講。
存儲元數(shù)據(jù)
在如何在 npm 上發(fā)布二進制文件?中,我們同樣有package.json,它既不是用于「依賴管理」也不是用于定義項目命令,而是通過一些字段的配置來說明包的名稱和版本信息,以及該包被用于那些操作系統(tǒng)和系統(tǒng)架構(gòu)。
也就是說,
package.json還可以存儲必要的包的元數(shù)據(jù)。
2. package.json 中關(guān)鍵字段
創(chuàng)建package.json
我們可以使用 npm init 命令在任意我們想創(chuàng)建前端項目的文件夾中創(chuàng)建一個 package.json 文件。當(dāng)我們運行該命令時,它會詢問我們一系列的問題,我們輸入的所有答案都會顯示在我們的 package.json 文件中。
圖片
當(dāng)我們填入我們想要的信息后,就會在項目的根目錄下創(chuàng)建一個package.json文件。
{
"name": "front789",
"version": "1.0.0",
"description": "專注于前端開發(fā)技術(shù)Rust及AI應(yīng)用知識分享的Coder",
"main": "index.js",
"scripts": {
"test": "vitest"
},
"repository": {
"type": "git",
"url": "https://xxx/xxx"
},
"keywords": ["前端開發(fā)", "rust", "ai"],
"author": "front789 (https://xxx/xx)",
"license": "MIT"
}
當(dāng)然我們也可以采用手動方式在項目根目錄下創(chuàng)建一個package.json并填入必要的信息
如果大家看過package.json的官網(wǎng)[2]的話,會發(fā)現(xiàn)有很多屬性,并用它使用場景五花八門。
而今天我們也不打算把這些屬性都過一遍,我會有選擇的挑選一些對我們平時開發(fā)有用的屬性來講。
name
name 字段用于標(biāo)識軟件包。
{
"name": "my-project"
}
在 package.json 文件中,name和version是「強制」的,它們一起被認(rèn)為是唯一的。例如,如果name是 f_cli,version是 0.1.0,那么就假定f_cli@0.1.0是唯一的,不指代任何其他軟件包。
軟件包name也是有一定的限制的。
?
它需要符合^(?:(?:@(?:[a-z0-9-*~][a-z0-9-*._~]*)?/[a-z0-9-._~])|[a-z0-9-~])[a-z0-9-._~]*$正則規(guī)則
?
- 它不能超過 214 個字符,并且只能包含小寫字母。
- 名稱不能以點(.)或下劃線(_)開頭。
- 此外,名稱通常是 URL 的一部分,因此必須是 URL 安全的。
軟件包名稱也可以是作用域(scope)的。例如,軟件包的名稱可以是@front/f_cli。這是@organization/package 的形式。但是這種形式時候需要花錢的。
如果將包發(fā)布到 npmjs,則 name 屬性是必需的并且必須是唯一的。如果使用和 npmjs已經(jīng)存在的名稱發(fā)布包,將收到錯誤。
如果不將包發(fā)布到npmjs,那么項目的name字段就沒有那么多要求。
version
{
"version": "1.1.0",
}
version 字段也是用于標(biāo)識軟件包的鍵。通常,此版本號必須是可由 node-semver[3] 解析的。
「語義化版本控制」(Semantic Versioning,SemVer)是一組用于版本控制的規(guī)則,以便版本號的更改表明軟件包中的變化類型。
版本號以 MAJOR.MINOR.PATCH 的形式編寫
- 如果新版本中有錯誤修復(fù),則增加 PATCH。
- 如果有新功能,則增加版本的 MINOR 部分。
- 如果新版本有破壞性變化或與舊版本不兼容,則增加版本的 MAJOR 部分。
例如,如果軟件包的當(dāng)前版本為 1.0.9:
- 如果下一個發(fā)布僅包含錯誤修復(fù),則新版本應(yīng)為 1.0.10。
- 如果下一個發(fā)布包含新功能,則新版本應(yīng)為 1.1.0。
- 如果下一個發(fā)布有破壞性變化,則新版本應(yīng)為 2.0.0。
description
description 字段簡要描述了「軟件包的功能」。它對于 SEO 也很有用,因為它幫助其他人可以在npmjs.com 網(wǎng)站上找到我們的軟件包。當(dāng)用戶在npmjs.com搜索包時,此字符串用于幫助顯示包。
就像我們f_li_darwin_arm64的包有如下的配置信息。
圖片
當(dāng)我們在npmjs中按照description搜索時也會返回對應(yīng)的結(jié)果。
圖片
即使我們沒有將其發(fā)布到npmjs,它也可以用作項目的說明書,來說明該項目的性質(zhì)和功能。起到一個「高屋建瓴」的作用。
keywords
{
"keywords": ["f_cli", "MACOS_Intel64"]
}
keywords 字段是一個「字符串?dāng)?shù)組」,與 description 類似,keywords 字段也用于 SEO。該字段由 npmjs編制索引,用于幫助在有人搜索包時找到包。數(shù)組中的每個值都是與我們的包關(guān)聯(lián)的一個關(guān)鍵字。
圖片
如果我們不發(fā)布到npmjs,則此字段沒有多大用處,可以隨意省略它。
homepage
{
"homepage": "https://github.com/owner/project#readme"
}
通常,我們會在此字段中鏈接到項目的網(wǎng)站地址?;蛘?,我們還可以指向項目的 README 或文檔。
bugs
{
"bugs": {
"url": "https://github.com/owner/project/issues",
"email": "project@hostname.com"
}
}
當(dāng)別人使用了我們的包,在遇到問題后可以依據(jù)這個字段提供的url和email來提出問題,并嘗試在需要的地方尋找解決方案。
如果我們不想提供支持電子郵件,我們可以直接將 URL 分配給 bugs 屬性。
{
"bugs": "https://github.com/owner/project/issues",
}
license
{
"license":"MIT"
}
license 是一個重要的字段,因為它向使用包的用戶描述了我們在使用此軟件包時設(shè)置的「權(quán)限」和「限制」。理想情況下,對于開源軟件包,許可證應(yīng)該是 OSI 批準(zhǔn)[4]的許可證之一。
下面是我們常見的OSI軟件開源協(xié)議。
許可證 | 版本 | 主要特點 |
GNU通用公共許可證(GPL) | 2.0 | 允許使用、修改、復(fù)制和分發(fā)軟件,要求在修改后的軟件中保持相同的GPL許可協(xié)議。 |
3.0 | 強調(diào)數(shù)字版權(quán)管理(DRM)和專利許可,以保障開源軟件的自由性和用戶權(quán)益。 | |
MIT許可證 | - | 允許幾乎所有情況下自由使用、修改、復(fù)制和分發(fā)軟件,只要在軟件和相關(guān)文檔中包含原許可協(xié)議和版權(quán)聲明。 |
Apache許可證 2.0 | - | 允許使用、修改、復(fù)制和分發(fā)軟件,要求在分發(fā)時保留原始許可協(xié)議和版權(quán)聲明,修改后的代碼必須以某種形式標(biāo)明更改。 |
BSD許可證 | 2-Clause | 允許自由使用、修改、復(fù)制和分發(fā)軟件,要求在分發(fā)時保留原始許可協(xié)議和版權(quán)聲明。 |
3-Clause | 在2-Clause BSD License的基礎(chǔ)上增加了對使用軟件名稱、作者名稱或衍生品的宣傳中的責(zé)任免除條款。 | |
GNU寬通用公共許可證(LGPL) | 2.1 | 允許在自由和開源項目中使用該軟件,并在修改后的庫中使用不同的許可協(xié)議。 |
3.0 | 強調(diào)數(shù)字版權(quán)管理(DRM)和專利許可,適用于開源項目,并在修改后的庫中使用不同的許可協(xié)議。 |
如果我們不希望在任何條件下向軟件包的用戶授予任何權(quán)限,可以將此字段設(shè)置為 UNLICENSED。
如果這個項目是我們公司的項目,并且也不準(zhǔn)備開源,應(yīng)該將 package.json 文件中的 private 字段設(shè)置為 true,以防止意外發(fā)布軟件包。
author
author 字段用于提供有關(guān)軟件包開發(fā)者的信息。它包括一個name,以及一個可選的email和 url 字段。
以下是一個示例:
{
"name": "front789",
"email": "xx@gmial.com",
"url": "https://xx.io"
}
所有這些信息也可以縮小成以下格式的單個字符串:
{
"author": "Name <Email> (Site)"
}
例如,我們可以使用此格式指定與上述相同的作者:
{
"author": "front789 <xx@gmial.com> (https://xx.io)"
}
contributors
與 author 字段類似,contributors 字段提供有關(guān)軟件包開發(fā)者的信息。它包含一個作者數(shù)組。
files
files 字段是一個「文件模式數(shù)組」,描述當(dāng)「我們的包作為依賴項安裝時要包含的文件」。
文件模式遵循與.gitignore 類似的語法。唯一的區(qū)別是在.gitignore 中指定的文件被排除在外,而這些文件被包括在內(nèi)。我們還可以使用像*和**/*這樣的 glob 模式,就像在.gitignore 文件中一樣。
如果未特別指定,則 files 字段默認(rèn)為["*"]。
我們可以在包的根目錄或子目錄中提供 .npmignore 文件,這將防止包含文件。 .npmignore 文件的工作方式與 .gitignore 類似。
一些特殊的文件和目錄也會被包含,無論它們是否存在于 files 數(shù)組中。
- package.json
- README
- LICENSE / LICENCE
- 在main字段指定的文件
- 在bin字段指定的文件
其中README 和 LICENSE/LICENCE 文件可以具有任何擴展名。
main
{
"main": "src/index.js",
}
main 字段是 package.json 的「功能屬性」。這定義了項目的入口點,通常也是用于啟動項目的文件。
如果我們的軟件包(假設(shè)其名稱為 front789)由用戶安裝,那么當(dāng)用戶執(zhí)行 require('front789') 時,則將返回主模塊的導(dǎo)出對象。
這通常是項目根目錄中的 index.js 文件,但它可以是我們選擇用作包的主入口的任何文件。
如果未設(shè)置 main,則默認(rèn)為包根文件夾中的 index.js
該字段在Node12+有另外的替代方案 - exports。
圖片
exports
我們可以使用 exports 字段定義軟件包的入口點,作為 main 字段的替代方案。與 main 不同,exports 允許我們定義子路徑導(dǎo)出和條件導(dǎo)出。
例如,我們可以使用以下 exports 屬性導(dǎo)出項目的 submodule.js 文件:
{
"exports": {
".": "./index.js",
"./submodule.js": "./src/submodule.js"
}
}
還可以條件導(dǎo)出 - 具體取決于軟件包的使用者是使用 require 還是 import。
{
"exports": {
"import": "./index-module.js",
"require": "./index-require.cjs"
},
"type": "module"
}
條件導(dǎo)出通常用于 ESM 軟件包,以確保向后兼容性,因為 import 關(guān)鍵字只能在 ESM 中使用。
type
此字段描述了當(dāng)前軟件包中的.js 文件應(yīng)該被視為 ESM 還是 commonjs。我們可以為 ESM 設(shè)置module類型,并為非ESM 軟件包設(shè)置 commonjs。
此外,我們還可以明確指定文件是否應(yīng)該解釋為 ESM 或 commonjs,使用.mjs 擴展名表示 ESM,.cjs擴展名表示 commonjs 文件。
{
"type": "module"
}
browser
此字段用于表示軟件包是否應(yīng)在瀏覽器中使用,而不是在 Node.js 項目中使用,以取代 main。當(dāng)我們的軟件包使用像 window 這樣的瀏覽器API,在 Node.js 環(huán)境中不可用時,就會使用它。
bin
這個我們很熟,在如何在 npm 上發(fā)布二進制文件?中,我們在定義主包時,就使用了bin字段。
bin 字段,該字段是「命令名」到「本地文件名」的映射。
在某些情況下,npm 軟件包需要安裝到 PATH 中,以便它們可以在任何目錄中直接由操作系統(tǒng)運行。bin 字段指定這些類似可執(zhí)行文件的文件。
當(dāng)此軟件包「全局安裝」時,該文件將鏈接到全局 bins 目錄內(nèi),或者將創(chuàng)建一個 cmd(Windows 命令文件)來執(zhí)行 bin 字段中的指定文件,因此可用于由 name 或 name.cmd(在 Windows PowerShell 上)運行。
當(dāng)我們在 bin 屬性中有以下配置。
{
"bin": {
"c1": "./r1.js",
"c2": "./r2.js"
}
}
在全局安裝此軟件包(使用 npm install -g)后,我們將能夠直接從終端運行 c1 和 c2 等命令。
- 這在 unix-like 操作系統(tǒng)上內(nèi)部創(chuàng)建了 r1.js 文件到/usr/local/bin/c1 的符號鏈接,以及 r2.js 到/usr/local/bin/c2 的符號鏈接。
- 在 Windows 上,會創(chuàng)建一個 C:\\Users\\{Username}\\AppData\\Roaming\\npm\\c1.cmd 文件,該文件運行 r1 腳本。
bin 屬性提到的文件,都以 shebang語法 #!/usr/bin/env node 開頭,否則我們的操作系統(tǒng)將不會意識到該文件應(yīng)在 Node.js 環(huán)境中運行。
shebang
shebang是一種在Unix/Linux系統(tǒng)中用于「指定腳本解釋器的約定」。在Node.js中,也可以使用shebang來指定Node.js作為腳本的解釋器。在腳本文件的開頭,添加類似于#!/usr/bin/env node的行,告訴操作系統(tǒng)使用Node.js來解釋執(zhí)行該腳本。
- #!:這是shebang的起始標(biāo)志,告訴操作系統(tǒng)下面的路徑是解釋器的路徑。
- /usr/bin/env:這是一個用于在環(huán)境變量中查找解釋器的工具。它允許你在不同系統(tǒng)上使用不同的解釋器路徑,而不是硬編碼一個固定的路徑。
- node:這是指定的解釋器的名稱。在這里,它告訴操作系統(tǒng)使用Node.js來解釋執(zhí)行腳本。
Node.js的shebang行告訴操作系統(tǒng)找到Node.js解釋器并使用它來執(zhí)行腳本。這使得腳本可以作為可執(zhí)行文件直接運行,而不必在命令行中顯式調(diào)用Node.js。
案例分析
還記得f_cli的npm版本嗎。
我們在package.json中的bin字段定義f_cli_f和bin/cli直接的關(guān)系
圖片
并且在bin/cli中使用shebang指定node.js作為腳本解釋器。
圖片
repository
該字段來記錄項目代碼所在的地址。該字段是一個對象,它定義了源代碼所在的 url,以及它使用的版本控制系統(tǒng)類型。對于開源項目,大部分都是GitHub,以 Git 作為版本控制系統(tǒng)。
{
"repository": {
"type": "git",
"url": "https://github.com/xxx/docs.git"
}
}
scripts
script 字段是 package.json 中另一個「功能性元數(shù)據(jù)」
scripts 屬性是一個包含我們可以使用 npm CLI 運行的腳本命令的「字典」。其鍵是我們可以使用 npm run <scriptName> 運行的腳本,值是實際運行的命令。這些通常是終端命令,我們將其放入腳本字段中,以便我們可以記錄它們并輕松地重用它們。
我們還可以指定在軟件包生命周期的不同時間運行的腳本。例如,我們可以添加一個 prepublish 腳本,在軟件包發(fā)布之前運行(當(dāng)我們運行 npm publish 時)。
這是一個比較大的主題,我們打算另起一篇,詳細(xì)介紹一下
dependencies
{
"dependencies": {
"A": "^4.16.4",
"B": "~1.7.4"
}
}
此處列出了我們的項目使用的所有依賴項。使用 npm cli 安裝軟件包時(npm install xxx@1.0.1),會將其下載到我們的 node_modules/ 文件夾中,并將添加到我們的依賴項屬性中,并注明軟件包的名稱(xxx)和安裝的版本(1.0.1)。
dependencies字段是一個對象,以包名稱作為鍵,以版本或版本范圍作為值。從這個列表中,npm 知道當(dāng) npm install 在目錄中運行時要獲取和安裝哪些包(以及什么版本)。 package.json 的 dependency 字段是項目的核心,它定義了項目所需的外部包。
我們在dependencies中看到的脫字號 (^) 和波形符 (~) 是 SemVer[5] 中定義的版本范圍的表示法。
在語義版本控制中,版本號由三個數(shù)字組成,格式如下:
`主版本號.次版本號.修訂號`
- 脫字符號(^)表示允許更新到最新的次版本號:
- ^1.2.3 表示 >=1.2.3 并且 <2.0.0 的最新版本
- 波形符(~)表示允許更新到最新的修訂號:
~1.2.3 表示 >=1.2.3 并且 <1.3.0 的最新版本
所以簡單來說:
- ^ 用于鎖定主版本號和次版本號
- ~ 用于鎖定主版本號和次版本號及修訂號
devDependencies
與 dependencies 字段類似,但適用于僅在開發(fā)期間需要而在生產(chǎn)中不需要的包。
例如,在前端項目中我們使用eslint/oxlint進行代碼規(guī)范處理,一旦應(yīng)用程序部署并投入生產(chǎn),我們就不再使用它。devDependencies 屬性讓我們明確指出生產(chǎn)中不需要哪些依賴項。
我們可以使用npm install --save-dev <package>安裝指定的包,并且將相關(guān)的包信息記錄到dependencies中。
但是呢,由于我們項目開發(fā)時,可以隨意引入外部包,有的同學(xué)也會將在開會環(huán)境中起作用的包安裝到dependencies中。這樣,無形中增加我們生產(chǎn)環(huán)境的外部資源容量。
為了解決這個問題,我們可以在生產(chǎn)環(huán)境中安裝應(yīng)用程序時,我們可以使用 npm install --production 僅安裝 package.json 的 dependency 字段中列出的內(nèi)容。
peerDependencies
peerDependencies 字段用于指定「一個包依賴于其他包的特定版本」。這是為了解決一種情況:當(dāng)一個包(插件或庫)希望與另一個包協(xié)同工作,但不希望將其作為直接依賴項安裝。
當(dāng)某個包 A 聲明了它的 peerDependencies,它實際上是在聲明:“我期望運行時環(huán)境中會有某個包 B 的特定版本,但我不會直接將 B 包添加為我的依賴項,而是期望它由運行時環(huán)境或其他上層的包提供。”
以下是一個簡單的示例,展示了 peerDependencies 的用法:
{
"name": "my-package",
"version": "1.0.0",
"peerDependencies": {
"some-other-package": "^2.0.0"
}
}
在這個例子中,my-package 依賴于 some-other-package 的版本,但它并沒有將其添加到 dependencies 中。相反,它期望運行時環(huán)境或上層的包會提供符合 ^2.0.0 版本要求的 some-other-package。
使用 peerDependencies 的主要目的是「確保在整個項目中使用相同版本的某個包,以防止出現(xiàn)不一致的依賴關(guān)系導(dǎo)致的問題」。這有助于確保包之間的協(xié)同工作,并降低由于版本不一致而引起的潛在問題。
peerDependenciesMeta
peerDependenciesMeta 字段用于提供 peerDependencies 的額外元數(shù)據(jù)信息,以幫助管理 peerDependencies。
它是一個對象,key 是 peerDependencies 的名字,value 是關(guān)于這個依賴的額外信息。
比如:
{
"peerDependencies": {
"react": ">=16.8.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
}
}
}
這里對 react 這個 peerDependency 提供了額外的元數(shù)據(jù):optional: true。
這個 optional 鍵指明這個 peerDependency 是可選的,如果使用者沒有顯式安裝它,也不會觸發(fā)警告。
這比直接把依賴設(shè)置為 optionalDependencies 要好,因為這樣所有的 peerDependency 都聚集在 peerDependencies 字段中,而不會分散在兩個地方。
peerDependenciesMeta 還可以用于提供其他元數(shù)據(jù),比如說明、地位等,以便更好地幫助使用者理解和管理 peerDependencies。
bundleDependencies
bundleDependencies字段用于指定將哪些依賴包打包到最終的發(fā)布包中。
比如:
{
"name": "example-package",
"version": "1.0.0",
"bundledDependencies": [
"lodash",
"moment"
]
}
這意味著當(dāng)這個包被發(fā)布時,lodash和moment這兩個依賴會被打包到發(fā)布包中,而不是作為外部依賴被安裝。
bundleDependencies的常見使用場景包括:
- 將核心業(yè)務(wù)邏輯相關(guān)的依賴打包起來,以控制版本和優(yōu)化性能
- 將大體積的依賴打包,以減少安裝時間
- 將易受破壞的依賴打包,以增強穩(wěn)定性
使用bundleDependencies需要注意:
- 打包的依賴無法被使用者覆蓋更新
- 會增加發(fā)布包的體積
- 需要同步維護依賴版本
所以需要根據(jù)實際情況權(quán)衡利弊來決定哪些依賴適合打包。
optionalDependencies
當(dāng)找不到或無法安裝依賴項時,npm install 命令會退出并顯示錯誤。如果特定軟件包存在于 optionalDependencies 而不是其他任何依賴項列表/字典中,則可以阻止出現(xiàn)此情況。
在 package.json 中,optionalDependencies 字段用于指定哪些依賴可以被視為可選的。
例如:
{
"optionalDependencies": {
"xx": "^1.0.0"
}
}
這表示 xx 這個依賴是可選的,如果安裝過程中無法滿足,npm 會繼續(xù)正常安裝,只是發(fā)出警告。
optionalDependencies 和 dependencies 的區(qū)別在于:
- dependencies 是必需的,如果安裝失敗會導(dǎo)致整個安裝過程終止。
- optionalDependencies 是可選的,如果安裝失敗會發(fā)出警告但不影響整體安裝。
optionalDependencies 的常見使用場景:
- 對某些特定平臺的依賴,如 fsevents 主要用于 MacOS。
- 一些可提升性能但不是必須的依賴。
- 一些實驗性特性相關(guān)的依賴。
使用 optionalDependencies 表示可選依賴,可以很好地提升使用者的安裝體驗,避免因為某些非核心依賴而導(dǎo)致整個安裝失敗。
還記得我們在發(fā)布f_cli時候,通過optionalDependencies來指定相關(guān)工作環(huán)境的二進制包嗎。
圖片
publishConfig
我們可以使用此選項指定軟件包是否應(yīng)該是公開可訪問的,以及軟件包使用哪個標(biāo)簽發(fā)布。默認(rèn)情況下,軟件包是私有的,并且默認(rèn)標(biāo)簽是 latest。使用不同的標(biāo)簽,例如 beta,允許用戶使用npm install <package-name>@beta安裝軟件包的特定版本。
在 package.json 中,publishConfig 用于配置發(fā)布這個包時的一些設(shè)置。
其中常見的配置有:
- registry: 發(fā)布到哪個注冊表,默認(rèn)是 https://registry.npmjs.org/
- tag: 發(fā)布時添加的 tag,默認(rèn)是 latest
例如:
{
"publishConfig": {
"registry": "https://私有注冊表網(wǎng)址",
"tag": "internal"
}
}
這意味著發(fā)布這個包時會發(fā)布到私有注冊表,并打上 internal 的 tag。
publishConfig 的常見使用場景:
- 指定私有注冊表,用于企業(yè)內(nèi)部發(fā)布包
- 為預(yù)發(fā)布的版本添加特殊 tag,如 next
- 發(fā)布到不同注冊表的同名包,用 tag 進行區(qū)分
所以 publishConfig 可以很好地自定義包的發(fā)布過程,將其發(fā)布到特定的注冊表或添加自定義標(biāo)簽。
另外,發(fā)布過程還可以通過 npm publish 命令的 --tag 參數(shù)動態(tài)配置。
workspaces
workspaces字段用于定義 monorepo 結(jié)構(gòu)中的多個 package。
例如:
{
"name": "monorepo",
"workspaces": [
"packages/*"
]
}
這表示packages目錄下的「所有子目錄都會被當(dāng)作獨立的 package,可以相互依賴和發(fā)布」。
啟用 workspaces 后,在根目錄運行類似npm install、npm run build等命令,會自動在所有 workspace 包中生效。
workspaces的常見使用場景:
- 管理多個相關(guān)的包,讓它們使用同一個git倉庫和配置
- 共享依賴以優(yōu)化安裝大小
- 統(tǒng)一管理命令和腳本配置
與普通的多包管理相比,workspaces 可以減少重復(fù)工作,大幅簡化 monorepo 開發(fā)。
需要注意的是,所有 workspace 需要遵循相同的npm包規(guī)則,如版本控制、發(fā)布模式等,以減少管理負(fù)擔(dān)。
這個選項在單體倉庫中非常有用。我們可以以以下方式指定工作空間的列表:
{
"workspaces": [
"./packages/client",
"./packages/server"
]
}
我們可以在 client 和 server 目錄中有單獨的 package.json 文件,其中包含單獨的腳本。運行 npm install --workspaces 將在兩個目錄中運行 npm install。
實際上,我們可以使用--workspaces 命令在指定的所有工作空間中運行任何腳本。
例如,如果在 packages/client 和 packages/server 中有單獨的 lint 腳本,在根 package.json 中,我們可以有一個 lint 腳本?,F(xiàn)在,如果在根目錄中運行 npm run lint --workspaces --if-present,它將在所有具有 lint 腳本的工作空間中運行 lint 腳本。
--if-present 保證即使有些包沒有 lint 腳本,也不會影響其它包的執(zhí)行。
lock文件
在我們的 npm 項目中安裝軟件包時,通常會出現(xiàn)一個神秘的 package-lock.json 文件。
正如名稱所示,package-lock.json 是一個鎖定文件,即一個「存儲了使用的軟件包及其所有依賴軟件包的確切版本號的文件」。這包括在我們的 node_modules 目錄中存在的所有軟件包。
該文件的目的是確保所有依賴項在不同的機器上以相同的方式安裝,從而保證項目在不同環(huán)境中能夠一致工作。
package-lock.json 文件還包括每個軟件包內(nèi)容的加密哈希,這確保安裝的軟件包未被篡改,并且與軟件包作者發(fā)布的確切相同的軟件包。
當(dāng)我們運行 npm install 時,npm 使用 package-lock.json 中的信息確定要安裝的軟件包的確切版本,并以與原始安裝相同的順序和相同的依賴項安裝它們。
其他包管理器
盡管 npm 是最流行的包管理器之一,但很多人也使用其他包管理器,如 yarn、pnpm 或 turbo。這些包管理器中仍然存在 package.json 文件,但不同的包管理器可能使用不同的名稱來命名鎖文件。例如,yarn 創(chuàng)建的 鎖文件 稱為 yarn.lock,看起來類似于以下內(nèi)容:
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
package-1@^1.0.0:
version "1.0.3"
resolved "https://registry.npmjs.org/package-1/-/package-1-1.0.3.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"
package-2@^2.0.0:
version "2.0.1"
resolved "https://registry.npmjs.org/package-2/-/package-2-2.0.1.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"
dependencies:
package-4 "^4.0.0"
package-3@^3.0.0:
version "3.1.9"
resolved "https://registry.npmjs.org/package-3/-/package-3-3.1.9.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"
dependencies:
package-4 "^4.5.0"
package-4@^4.0.0, package-4@^4.5.0:
version "4.6.3"
resolved "https://registry.npmjs.org/package-4/-/package-4-2.6.3.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"
類似地,pnpm 生成一個稱為 pnpm-lock.yaml 的 鎖文件。然而,所有這些 lockfile 的目的與 npm 生成的 package-lock.json 文件相同。
3. 總結(jié)
總的來說,package.json是 Node.js 開發(fā)中使用的重要元數(shù)據(jù)文件。它有助于管理依賴關(guān)系、自動化任務(wù)并配置項目。該文件包含了項目名稱、版本號、作者、許可證、依賴關(guān)系等基本信息。
通過使用 package.json,我們可以輕松管理項目所需的依賴項,確保安裝每個軟件包的正確版本。這使得更容易維護項目并在必要時更新依賴項。
此外,它還可以用于自動化任務(wù),如構(gòu)建項目、運行測試和啟動應(yīng)用程序。這可以為我們節(jié)省時間和精力,使他們能夠?qū)W⒂陧椖康母匾矫妗?/p>
最后,它允許我們將其項目發(fā)布到 npmjs,使其他用戶能夠輕松安裝和在自己的項目中使用該項目。