這還是我最熟悉的package.json嗎?
前言
在上一篇npm init @vitejs/app的背后,僅是npm CLI的冰山一角[1]中,有提到我復(fù)習(xí)npm主要是從兩個(gè)大方向來(lái)入手,所以這篇繼續(xù)來(lái)講講package.json這部分知識(shí),經(jīng)過(guò)這輪復(fù)習(xí),也發(fā)現(xiàn)了自己的很多不足,之前把常用的命令和配置玩熟了,卻沒(méi)關(guān)心npm已經(jīng)有了更多新的玩法,而這些玩法卻實(shí)實(shí)在在地在解決別人的問(wèn)題。
npm 的配置還是挺多的,具體可以參考package.json官方文檔[2]。通讀了文檔之后,我略過(guò)了一些基礎(chǔ)的配置項(xiàng),總結(jié)了一些我認(rèn)為比較有用的配置項(xiàng)。
常用配置項(xiàng)
files
files定義了哪些文件應(yīng)該被包括在 npm install 后的 node_modules中。
當(dāng)然,有些文件是自動(dòng)暴露出來(lái)的,不管你是不是配置了files,比如:
- package.json
- README / CHANGELOG / LICENSE
- ...
很多庫(kù)都定義了 files,避免一些不必要的文件暴露到 node_modules 中。
vite 中是這樣配置的:
- {
- "files": [ "bin", "dist", "client.d.ts" ]
- }
我之前就不知道這個(gè)配置,導(dǎo)致我發(fā)布的一個(gè) npm 組件 vue-awesome-progress[3] 就暴露了源碼部分,雖然這也沒(méi)啥影響,本來(lái)就是開(kāi)源的。但是這也增加了別人的資源下載量,也是一種浪費(fèi)。所以,專(zhuān)業(yè)點(diǎn)的搞法還是加上files配置吧。
bin
bin 列出了可執(zhí)行文件,表示你這個(gè)包要對(duì)外提供哪些腳本。
在這個(gè)包被 install 安裝時(shí),如果是全局安裝 -g,bin 列出的可執(zhí)行文件會(huì)被添加到 PATH 變量(全局可執(zhí)行);如果是局部安裝,則會(huì)進(jìn)入到 node_modules/.bin/ 目錄下。
bin 在一些 CLI 工具中用得很頻繁,比如 Vue CLI。
在開(kāi)發(fā) npm 包時(shí),要求發(fā)布的可執(zhí)行腳本要以#!/usr/bin/env node開(kāi)頭,這是為什么呢?
我查了一下,原來(lái)是為了用于指明該腳本文件要使用 node 來(lái)執(zhí)行。
main, browser, module
這三個(gè)配置對(duì)我們的影響還是挺大的。
main字段決定了別人require('xxx')時(shí),引用的是哪個(gè)模塊對(duì)象。在不設(shè)置main字段時(shí),默認(rèn)值是index.js。
如果你開(kāi)發(fā)的包是用于瀏覽器端的,那么用browser指定入口文件是最佳的選擇。
module則代表你開(kāi)發(fā)的包支持ESM,并指定了一個(gè)ESM入口。
具體這三個(gè)字段怎么用,還是挺有學(xué)問(wèn)的,這里推薦一篇文章package.json中你還不清楚的browser,module,main 字段優(yōu)先級(jí)[4],講得挺細(xì)。
長(zhǎng)圖警告!
scripts
scripts也基本上每天都用了,但是它的鉤子腳本你用過(guò)嗎?如果沒(méi)有用過(guò),可以試試,在組織腳本流程時(shí)非常好用!
- pre:在一個(gè)script執(zhí)行前執(zhí)行,比如prebuild,可以在打包前做一些準(zhǔn)備工作。
- post:在一個(gè)script執(zhí)行后執(zhí)行,比如postbuild,可以在打包后做一些收尾工作。
config
通過(guò)config配置的參數(shù)xxx,可以在腳本中通過(guò)npm_package_config_xxx 的形式引用,比如port。
- {
- "config": {
- "port": "8080"
- }
- }
依賴(lài)相關(guān)
dependencies
dependencies可以理解為生產(chǎn)依賴(lài),通過(guò)npm install --save安裝的依賴(lài)包都會(huì)進(jìn)入到dependencies中。
devDependencies
devDependencies可以理解為開(kāi)發(fā)環(huán)境依賴(lài),通常是一些工具類(lèi)的包,比如 webpack, babel等。通過(guò)npm install --save-dev安裝的依賴(lài)包都會(huì)進(jìn)入到devDependencies中。
但是,在結(jié)合一些構(gòu)建工具使用時(shí),我們往往會(huì)有困惑。比如我安裝了一個(gè)包到devDependencies中,但是不小心在項(xiàng)目中引用了它,最后也被 webpack 打包到構(gòu)建結(jié)果中了。這是怎么回事呢?
建議結(jié)合上篇文章npm install這一節(jié)[5]一起看。
peerDependencies
我是package-a,你裝我,你就必須裝我的peerDependencies。
讓“調(diào)包俠”將package-a的依賴(lài)提升到自己的node_modules中,這樣可以在“調(diào)包俠”和package-a都需要同一個(gè)依賴(lài)(比如vue)時(shí),避免重復(fù)安裝。這常見(jiàn)于開(kāi)發(fā)組件或者庫(kù)。
注意,一個(gè) npm 包的開(kāi)發(fā)者如果聲明了peerDependencies,開(kāi)發(fā)環(huán)境下在該包目錄npm install也不會(huì)在node_modules中安裝這些依賴(lài),所以往往還需要借助devDependencies。
舉個(gè)例子,我開(kāi)發(fā)一個(gè)組件,不想發(fā)布到 npm 時(shí)包含了 vue 的代碼,這就需要外部提供 vue ,所以我把 vue 定義在 peerDependencies 也無(wú)可厚非。但是,在開(kāi)發(fā)組件時(shí),一般還需要本地開(kāi)發(fā)環(huán)境跑一個(gè) demo 試試效果,這時(shí)候是依賴(lài) vue 的,所以還需要在 devDependencies 中安裝 vue 。我看了下 vue-router 就是這么做的,所以我在開(kāi)發(fā)自己的組件時(shí)也學(xué)會(huì)了這招。
bundledDependencies
bundledDependencies跟上面的依賴(lài)都不太一樣,配置上不是鍵值對(duì)的形式,而是一個(gè)數(shù)組。
- {
- "bundledDependencies": [
- "vue",
- "vue-router"
- ]
- }
在運(yùn)行npm pack時(shí),會(huì)將對(duì)應(yīng)依賴(lài)打包到tgz文件中。用得不多,不知道具體的細(xì)節(jié),主要還是直接用npm install安裝 tgz 包的場(chǎng)景比較少,有個(gè)概念就行。
optionalDependencies
optionalDependencies用于配置可選的依賴(lài),即使配了這個(gè),代碼里也要做好判斷(保護(hù)),否則運(yùn)行報(bào)錯(cuò)就不好玩了。
- try {
- var foo = require('foo')
- var fooVersion = require('foo/package.json').version
- } catch (er) {
- foo = null
- }
題外話
仔細(xì)讀過(guò)package.json文檔后,整體上還是解決了我的不少困惑,對(duì)我開(kāi)發(fā) npm 組件也提供了不少幫助。如果您想了解更多細(xì)節(jié)和實(shí)戰(zhàn),不妨打開(kāi)我這個(gè)項(xiàng)目vue-awesome-progress[3]看看,希望對(duì)您有所幫助!