掃二維碼與項(xiàng)目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
今天來看看前端的大管家package.json文件相關(guān)的配置,充分了解這些配置有助于我們提高開發(fā)的效率,規(guī)范我們的項(xiàng)目。

在每個前端項(xiàng)目中,都有package.json文件,它是項(xiàng)目的配置文件,常見的配置有配置項(xiàng)目啟動、打包命令,聲明依賴包等。package.json文件是一個JSON對象,該對象的每一個成員就是當(dāng)前項(xiàng)目的一項(xiàng)設(shè)置。package.json作為前端的大管家,到底有哪些配置和我們的日常開發(fā)密切相關(guān)?下面就來仔細(xì)剖析一下這個文件。
當(dāng)我們搭建一個新項(xiàng)目時,往往腳手架就幫我們初始化好了一個package.jaon配置文件,它位于項(xiàng)目的根目錄中。當(dāng)使用react腳手架(create-react-app)初始化一個項(xiàng)目時,其package.json文件內(nèi)容如下:
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3",
"web-vitals": "^1.1.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}當(dāng)我們克隆一個新的項(xiàng)目到本地時,需要執(zhí)行npm install(yarn install)命令來安裝項(xiàng)目所需的依賴文件。當(dāng)執(zhí)行該命令時,就會根據(jù)package.json文件中的配置信息來自動下載所需的模塊,也就是配置項(xiàng)目所需的運(yùn)行和開發(fā)環(huán)境。
package.json 常見配置項(xiàng)如下:
package.json中最重要的兩個字段就是name和version,它們都是必須的,如果沒有,就無法正常執(zhí)行npm install命令。npm規(guī)定package.json文件是由名稱和版本號作為唯一標(biāo)識符的。
name很容易理解,就是項(xiàng)目的名稱,它是一個字符串。在給name字段命名時,需要注意以下幾點(diǎn):
如果npm包上有對應(yīng)的包,則會顯示包的詳細(xì)信息:
實(shí)際上,我們平時開發(fā)的很多項(xiàng)目并不會發(fā)布在npm上,所以這個名稱是否標(biāo)準(zhǔn)可能就不是那么重要,它不會影響項(xiàng)目的正常運(yùn)行。如果需要發(fā)布在npm上,name字段一定要符合要求。
version字段表示該項(xiàng)目包的版本號,它是一個字符串。在每次項(xiàng)目改動后,即將發(fā)布時,都要同步的去更改項(xiàng)目的版本號。版本號的使用規(guī)范如下:
可以通過以下命令來查看npm包的版本信息,以react為例:
// 查看最新版本
npm view react version
// 查看所有版本
npm view react versions
當(dāng)執(zhí)行第二條命令時,結(jié)果如下:
package.jaon中有五個和項(xiàng)目包描述信息相關(guān)的配置字段,下面就分別來看看這些字段的含義。
description字段用來描述這個項(xiàng)目包,它是一個字符串,可以讓其他開發(fā)者在 npm 的搜索中發(fā)現(xiàn)我們的項(xiàng)目包。
keywords字段是一個字符串?dāng)?shù)組,表示這個項(xiàng)目包的關(guān)鍵詞。和description一樣,都是用來增加項(xiàng)目包的曝光率的。下面是eslint包的描述和關(guān)鍵詞:
author顧名思義就是作者,表示該項(xiàng)目包的作者。它有兩種形式,一種是字符串格式:
"author": "CUGGZ(https://juejin.cn/user/3544481220801815)"
另一種是對象形式:
"author": {
"name" : "CUGGZ",
"email" : "[email protected]",
"url" : "https://juejin.cn/user/3544481220801815"
}contributors表示該項(xiàng)目包的貢獻(xiàn)者,和author不同的是,該字段是一個數(shù)組,包含所有的貢獻(xiàn)者,它同樣有兩種寫法:
"contributors": [
"CUGGZ0(https://juejin.cn/user/3544481220801815)",
"CUGGZ1(https://juejin.cn/user/3544481220801815)"
]
"contributors": [
{
"name" : "CUGGZ0",
"email" : "[email protected]",
"url" : "https://juejin.cn/user/3544481220801815"
},
{
"name" : "CUGGZ1",
"email" : "[email protected]",
"url" : "https://juejin.cn/user/3544481220801815"
}
]
homepage就是項(xiàng)目的主頁地址了,它是一個字符串。
repository表示代碼的存放倉庫地址,通常有兩種書寫形式。第一種是字符串形式:
"repository": "https://github.com/facebook/react.git"
除此之外,還可以顯式地設(shè)置版本控制系統(tǒng),這時就是對象的形式:
"repository": {
"type": "git",
"url": "https://github.com/facebook/react.git"
}bugs表示項(xiàng)目提交問題的地址,該字段是一個對象,可以添加一個提交問題的地址和反饋的郵箱:
"bugs": {
"url" : "https://github.com/facebook/react/issues",
"email" : "[email protected]"
}最常見的bugs就是Github中的issues頁面,如上就是react的issues頁面地址。
通常情況下,我們的項(xiàng)目會依賴一個或者多個外部的依賴包,根據(jù)依賴包的不同用途,可以將他們配置在下面的五個屬性下:dependencies、devDependencies、peerDependencies、bundledDependencies、optionalDependencies 。下面就來看看每個屬性的含義。
dependencies字段中聲明的是項(xiàng)目的生產(chǎn)環(huán)境中所必須的依賴包。當(dāng)使用 npm 或 yarn 安裝npm包時,該npm包會被自動插入到此配置項(xiàng)中:
npm install
yarn add
當(dāng)在安裝依賴時使用--save參數(shù),也會將新安裝的npm包寫入dependencies屬性。
npm install --save
該字段的值是一個對象,該對象的各個成員,分別由模塊名和對應(yīng)的版本要求組成,表示依賴的模塊及其版本范圍。
"dependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3",
},這里每一項(xiàng)配置都是一個鍵值對(key-value), key表示模塊名稱,value表示模塊的版本號。版本號遵循主版本號.次版本號.修訂號的格式規(guī)定:
需要注意,不要把測試或者過渡性的依賴放在dependencies,避免生產(chǎn)環(huán)境出現(xiàn)意外的問題。
devDependencies中聲明的是開發(fā)階段需要的依賴包,如Webpack、Eslint、Babel等,用于輔助開發(fā)。它們不同于 dependencies,因?yàn)樗鼈冎恍璋惭b在開發(fā)設(shè)備上,而無需在生產(chǎn)環(huán)境中運(yùn)行代碼。當(dāng)打包上線時并不需要這些包,所以可以把這些依賴添加到 devDependencies 中,這些依賴依然會在本地指定 npm install 時被安裝和管理,但是不會被安裝到生產(chǎn)環(huán)境中。
當(dāng)使用 npm 或 yarn 安裝軟件包時,指定以下參數(shù)后,新安裝的npm包會被自動插入到此列表中:
npm install --save-dev
yarn add --dev
"devDependencies": {
"autoprefixer": "^7.1.2",
"babel-core": "^6.22.1"
}有些情況下,我們的項(xiàng)目和所依賴的模塊,都會同時依賴另一個模塊,但是所依賴的版本不一樣。比如,我們的項(xiàng)目依賴A模塊和B模塊的1.0版,而A模塊本身又依賴B模塊的2.0版。大多數(shù)情況下,這不是問題,B模塊的兩個版本可以并存,同時運(yùn)行。但是,有一種情況,會出現(xiàn)問題,就是這種依賴關(guān)系將暴露給用戶。
最典型的場景就是插件,比如A模塊是B模塊的插件。用戶安裝的B模塊是1.0版本,但是A插件只能和2.0版本的B模塊一起使用。這時,用戶要是將1.0版本的B的實(shí)例傳給A,就會出現(xiàn)問題。因此,需要一種機(jī)制,在模板安裝的時候提醒用戶,如果A和B一起安裝,那么B必須是2.0模塊。
peerDependencies字段就是用來供插件指定其所需要的主工具的版本。
"name": "chai-as-promised",
"peerDependencies": {
"chai": "1.x"
}
上面代碼指定在安裝chai-as-promised模塊時,主程序chai必須一起安裝,而且chai的版本必須是1.x。如果項(xiàng)目指定的依賴是chai的2.0版本,就會報錯。
需要注意,從npm 3.0版開始,peerDependencies不再會默認(rèn)安裝了。
如果需要在找不到包或者安裝包失敗時,npm仍然能夠繼續(xù)運(yùn)行,則可以將該包放在optionalDependencies對象中,optionalDependencies對象中的包會覆蓋dependencies中同名的包,所以只需在一個地方進(jìn)行設(shè)置即可。
需要注意,由于optionalDependencies中的依賴可能并為安裝成功,所以一定要做異常處理,否則當(dāng)獲取這個依賴時,如果獲取不到就會報錯。
上面的幾個依賴相關(guān)的配置項(xiàng)都是一個對象,而bundledDependencies配置項(xiàng)是一個數(shù)組,數(shù)組里可以指定一些模塊,這些模塊將在這個包發(fā)布時被一起打包。
需要注意,這個字段數(shù)組中的值必須是在dependencies, devDependencies兩個里面聲明過的包才行。
當(dāng)我們維護(hù)一些舊項(xiàng)目時,可能對npm包的版本或者Node版本有特殊要求,如果不滿足條件就可能無法將項(xiàng)目跑起來。為了讓項(xiàng)目開箱即用,可以在engines字段中說明具體的版本號:
"engines": {
"node": ">=8.10.3 <12.13.0",
"npm": ">=6.9.0"
}需要注意,engines只是起一個說明的作用,即使用戶安裝的版本不符合要求,也不影響依賴包的安裝。
scripts 是 package.json中內(nèi)置的腳本入口,是key-value鍵值對配置,key為可運(yùn)行的命令,可以通過 npm run 來執(zhí)行命令。除了運(yùn)行基本的scripts命令,還可以結(jié)合pre和post完成前置和后續(xù)操作。先來看一組scripts:
"scripts": {
"dev": "node index.js",
"predev": "node beforeIndex.js",
"postdev": "node afterIndex.js"
}這三個js文件中都有一句console:
// index.js
console.log("scripts: index.js")
// beforeIndex.js
console.log("scripts: before index.js")
// afterIndex.js
console.log("scripts: after index.js")
當(dāng)我們執(zhí)行npm run dev命令時,輸出結(jié)果如下:
scripts: before index.js
scripts: index.js
scripts: after index.js
可以看到,三個命令都執(zhí)行了,執(zhí)行順序是predev→dev→postdev。如果scripts命令存在一定的先后關(guān)系,則可以使用這三個配置項(xiàng),分別配置執(zhí)行命令。
通過配置scripts屬性,可以定義一些常見的操作命令:
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"unit": "jest --config test/unit/jest.conf.js --coverage",
"test": "npm run unit",
"lint": "eslint --ext .js,.vue src test/unit",
"build": "node build/build.js"
}這些腳本是命令行應(yīng)用程序??梢酝ㄟ^調(diào)用 npm run XXX 或 yarn XXX 來運(yùn)行它們,其中 XXX 是命令的名稱。例如:npm run dev。我們可以為命令使用任何的名稱,腳本也可以是任何操作。
使用好該字段可以大大的提升開發(fā)效率。
config字段用來配置scripts運(yùn)行時的配置參數(shù),如下所示:
"config": {
"port": 3000
}如果運(yùn)行npm run start,則port字段會映射到npm_package_config_port環(huán)境變量中:
console.log(process.env.npm_package_config_port) // 3000
用戶可以通過npm config set foo:port 3001 命令來重寫port的值。
下面來看看package.json中和文件以及目錄相關(guān)的屬性。
main 字段用來指定加載的入口文件,在 browser 和 Node 環(huán)境中都可以使用。如果我們將項(xiàng)目發(fā)布為npm包,那么當(dāng)使用 require 導(dǎo)入npm包時,返回的就是main字段所列出的文件的module.exports 屬性。如果不指定該字段,默認(rèn)是項(xiàng)目根目錄下的index.js。如果沒找到,就會報錯。
該字段的值是一個字符串:
"main": "./src/index.js",
browser字段可以定義 npm 包在 browser 環(huán)境下的入口文件。如果 npm 包只在 web 端使用,并且嚴(yán)禁在 server 端使用,使用 browser 來定義入口文件。
"browser": "./src/index.js"
module字段可以定義 npm 包的 ESM 規(guī)范的入口文件,browser 環(huán)境和 node 環(huán)境均可使用。如果 npm 包導(dǎo)出的是 ESM 規(guī)范的包,使用 module 來定義入口文件。
"module": "./src/index.mjs",
需要注意,*.js 文件是使用 commonJS 規(guī)范的語法(require('xxx')),*.mjs 是用 ESM 規(guī)范的語法(import 'xxx')。
上面三個的入口入口文件相關(guān)的配置是有差別的,特別是在不同的使用場景下。在Web環(huán)境中,如果使用loader加載ESM(ES module),那么這三個配置的加載順序是browser→module→main,如果使用require加載CommonJS模塊,則加載的順序?yàn)閙ain→module→browser。
Webpack在進(jìn)行項(xiàng)目構(gòu)建時,有一個target選項(xiàng),默認(rèn)為Web,即構(gòu)建Web應(yīng)用。如果需要編譯一些同構(gòu)項(xiàng)目,如node項(xiàng)目,則只需將webpack.config.js的target選項(xiàng)設(shè)置為node進(jìn)行構(gòu)建即可。如果再Node環(huán)境中加載CommonJS模塊,或者ESM,則只有main字段有效。
bin字段用來指定各個內(nèi)部命令對應(yīng)的可執(zhí)行文件的位置:
"bin": {
"someTool": "./bin/someTool.js"
}這里,someTool 命令對應(yīng)的可執(zhí)行文件為 bin 目錄下的 someTool.js,someTool.js會建立符號鏈接node_modules/.bin/someTool。由于node_modules/.bin/目錄會在運(yùn)行時加入系統(tǒng)的PATH變量,因此在運(yùn)行npm時,就可以不帶路徑,直接通過命令來調(diào)用這些腳本。因此,下面的寫法可以簡寫:
scripts: {
start: './node_modules/bin/someTool.js build'
}
// 簡寫
scripts: {
start: 'someTool build'
}所有node_modules/.bin/目錄下的命令,都可以用npm run [命令]的格式運(yùn)行。
上面的配置在package.json包中提供了一個映射到本地文件名的bin字段,之后npm包將鏈接這個文件到prefix/fix里面,以便全局引入?;蛘哝溄拥奖镜氐膎ode_modules/.bin/文件中,以便在本項(xiàng)目中使用。
files配置是一個數(shù)組,用來描述當(dāng)把npm包作為依賴包安裝時需要說明的文件列表。當(dāng)npm包發(fā)布時,files指定的文件會被推送到npm服務(wù)器中,如果指定的是文件夾,那么該文件夾下面所有的文件都會被提交。
"files": [
"LICENSE",
"Readme.md",
"index.js",
"lib/"
]
如果有不想提交的文件,可以在項(xiàng)目根目錄中新建一個.npmignore文件,并在其中說明不需要提交的文件,防止垃圾文件推送到npm上。這個文件的形式和.gitignore類似。寫在這個文件中的文件即便被寫在files屬性里也會被排除在外。比如可以在該文件中這樣寫:
node_modules
.vscode
build
.DS_Store
man 命令是 Linux 中的幫助指令,通過該指令可以查看 Linux 中的指令幫助、配置文件幫助和編程幫助等信息。如果 node.js 模塊是一個全局的命令行工具,在 package.json 通過 man 屬性可以指定 man 命令查找的文檔地址:
"man": [
"./man/npm-access.1",
"./man/npm-audit.1"
]
man 字段可以指定一個或多個文件, 當(dāng)執(zhí)行man {包名}時, 會展現(xiàn)給用戶文檔內(nèi)容。
需要注意:
對于上面的配置,可以使用以下命令來執(zhí)行查看文檔:
man npm-access
man npm-audit
directories字段用來規(guī)范項(xiàng)目的目錄。node.js 模塊是基于 CommonJS 模塊化規(guī)范實(shí)現(xiàn)的,需要嚴(yán)格遵循 CommonJS 規(guī)范。模塊目錄下除了必須包含包項(xiàng)目描述文件 package.json 以外,還需要包含以下目錄:
在實(shí)際的項(xiàng)目目錄中,我們可能沒有按照這個規(guī)范進(jìn)行命名,那么就可以在directories字段指定每個目錄對應(yīng)的文件路徑:
"directories": {
"bin": "./bin",
"lib": "./lib",
"doc": "./doc",
"test" "./test",
"man": "./man"
},這個屬性實(shí)際上沒有什么實(shí)際的作用,當(dāng)然不排除未來會有什么比較有意義的用處。
下面來看看和npm項(xiàng)目包發(fā)布相關(guān)的配置。
private字段可以防止我們意外地將私有庫發(fā)布到npm服務(wù)器。只需要將該字段設(shè)置為true:
"private": true
preferGlobal字段表示當(dāng)用戶不把該模塊安裝為全局模塊時,如果設(shè)置為true就會顯示警告。它并不會真正的防止用戶進(jìn)行局部的安裝,只是對用戶進(jìn)行提示,防止產(chǎn)生誤解:
"preferGlobal": true
publishConfig配置會在模塊發(fā)布時生效,用于設(shè)置發(fā)布時一些配置項(xiàng)的集合。如果不想模塊被默認(rèn)標(biāo)記為最新,或者不想發(fā)布到公共倉庫,可以在這里配置tag或倉庫地址。更詳細(xì)的配置可以參考 npm-config。
通常情況下,publishConfig會配合private來使用,如果只想讓模塊發(fā)布到特定npm倉庫,就可以這樣來配置:
"private": true,
"publishConfig": {
"tag": "1.1.0",
"registry": "https://registry.npmjs.org/",
"access": "public"
}
os字段可以讓我們設(shè)置該npm包可以在什么操作系統(tǒng)使用,不能再什么操作系統(tǒng)使用。如果我們希望開發(fā)的npm包只運(yùn)行在linux,為了避免出現(xiàn)不必要的異常,建議使用Windows系統(tǒng)的用戶不要安裝它,這時就可以使用os配置:
"os" ["linux"] // 適用的操作系統(tǒng)
"os" ["!win32"] // 禁用的操作系統(tǒng)
該配置和OS配置類似,用CPU可以更準(zhǔn)確的限制用戶的安裝環(huán)境:
"cpu" ["x64", "AMD64"] // 適用的cpu
"cpu" ["!arm", "!mips"] // 禁用的cpu
可以看到,黑名單和白名單的區(qū)別就是,黑名單在前面加了一個“!”。
license 字段用于指定軟件的開源協(xié)議,開源協(xié)議表述了其他人獲得代碼后擁有的權(quán)利,可以對代碼進(jìn)行何種操作,何種操作又是被禁止的。常見的協(xié)議如下:
可以這樣來聲明該字段:
"license": "MIT"
package.json 文件還可以承載命令特有的配置,例如 Babel、ESLint 等。它們每個都有特有的屬性,例如 eslintConfig、babel 等。它們是命令特有的,可以在相應(yīng)的命令/項(xiàng)目文檔中找到如何使用它們。下面來看幾個常用的第三方配置項(xiàng)。
typings字段用來指定TypeScript的入口文件:
"typings": "types/index.d.ts",
該字段的作用和main配置相同。
eslint的配置可以寫在單獨(dú)的配置文件.eslintrc.json 中,也可以寫在package.json文件的eslintConfig配置項(xiàng)中。
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"rules": {},
"parserOptions": {
"parser": "babel-eslint"
},
}babel用來指定Babel的編譯配置,代碼如下:
"babel": {
"presets": ["@babel/preset-env"],
"plugins": [...]
}使用該字段可以讓 npm 上所有的文件都開啟 cdn 服務(wù),該CND服務(wù)由unpkg提供:
"unpkg": "dist/vue.js"
lint-staged是一個在Git暫存文件上運(yùn)行l(wèi)inters的工具,配置后每次修改一個文件即可給所有文件執(zhí)行一次lint檢查,通常配合gitHooks一起使用。
"lint-staged": {
"*.js": [
"eslint --fix",
"git add"
]
}使用lint-staged時,每次提交代碼只會檢查當(dāng)前改動的文件。
gitHooks用來定義一個鉤子,在提交(commit)之前執(zhí)行ESlint檢查。在執(zhí)行l(wèi)int命令后,會自動修復(fù)暫存區(qū)的文件。修復(fù)之后的文件并不會存儲在暫存區(qū),所以需要用git add命令將修復(fù)后的文件重新加入暫存區(qū)。在執(zhí)行pre-commit命令之后,如果沒有錯誤,就會執(zhí)行g(shù)it commit命令:
"gitHooks": {
"pre-commit": "lint-staged"
}這里就是配合上面的lint-staged來進(jìn)行代碼的檢查操作。
browserslist字段用來告知支持哪些瀏覽器及版本。Babel、Autoprefixer 和其他工具會用到它,以將所需的 polyfill 和 fallback 添加到目標(biāo)瀏覽器。比如最上面的例子中的該字段值:
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}這里指定了一個對象,里面定義了生產(chǎn)環(huán)境和開發(fā)環(huán)境的瀏覽器要求。上面的development就是指定開發(fā)環(huán)境中支持最后一個版本的chrome、Firefox、safari瀏覽器。這個屬性是不同的前端工具之間共用目標(biāo)瀏覽器和 node 版本的配置工具,被很多前端工具使用,比如Babel、Autoprefixer等。

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流