這篇文章帶大家了解一下node專案中的package.json設定文件,聊聊package.json中一些常見配置屬性、環境相關屬性、依賴相關屬性和三方屬性,希望對大家有幫助!
npm是前端開發人員廣泛使用的套件管理工具,專案中透過package.json來管理專案中所依賴的npm套件的配置。 package.json就是一個json文件,除了能夠描述專案的套件依賴外,允許我們使用「語意化版本規則」指明你專案依賴套件的版本,讓你的建置更好地與其他開發者分享,便於重複使用。 本文主要從最近的實踐出發,結合最新的npm和node的版本,介紹一下package.json中一些常見的配置以及如何寫一個規範的package.json
1. package.json簡介
在nodejs專案中,package.json是管理其依賴的配置文件,通常我們在初始化一個nodejs專案的時候會通過:
npm init
然後在你的目錄下會產生3個目錄/文件, node_modules, package.json和package.lock.json。其中package.json的內容為:
{ "name": "Your project name", "version": "1.0.0", "description": "Your project description", "main": "app.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", }, "author": "Author name", "license": "ISC", "dependencies": { "dependency1": "^1.4.0", "dependency2": "^1.5.2" } }
上述可以看出,package.json中包含了項目本身的元資料,以及項目的子依賴資訊(例如dependicies等)。
2. package-lock.json
我們發現在npm init的時候,不僅產生了package.json文件,還產生了package-lock.json檔案。那為什麼存在package.json的清空下,還需要產生package-lock.json檔案呢。本質上package-lock.json檔案是為了鎖版本,在package.json中指定的子npm套件例如:react: "^16.0.0",在實際安裝中,只要高於react的版本都滿足package.json的要求。這樣就使得根據同一個package.json文件,兩次安裝的子依賴版本不能保證一致。
而package-lock檔案如下所示,子依賴dependency1就詳細的指定了其版本。起到lock版本的作用。
{ "name": "Your project name", "version": "1.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { "dependency1": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/dependency1/-/dependency1-1.4.0.tgz", "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" }, "dependency2": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/dependency2/-/dependency2-1.5.2.tgz", "integrity": "sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ==" } } }
本章聊聊package.json中常用的設定屬性,形如name,version等屬性太簡單,不一一介紹。本章主要介紹一下script、bin和workspaces屬性。
2.1 script
在npm中使用script標籤定義腳本,每當制定npm run的時候,就會自動建立一個shell腳本,這裡要注意的是,npm run新建的這個Shell,會將本地目錄的node_modules/.bin子目錄加入PATH變數。
這意味著,目前目錄的node_modules/.bin子目錄裡面的所有腳本,都可以直接用腳本名稱調用,而不必加上路徑。例如,目前專案的依賴裡面有 esbuild,只要直接寫esbuild xxx 就可以了。
{ // ... "scripts": { "build": "esbuild index.js", } }
{ // ... "scripts": { "build": "./node_modules/.bin/esbuild index.js" } }
上面兩種寫法是等價的。
2.2 bin
bin屬性用來將執行檔載入到全域環境中,指定了bin欄位的npm包,一旦在全域安裝,就會被載入到全域環境中,可以透過別名來執行該檔案。
例如@bytepack/cli的npm套件:
"bin": { "bytepack": "./bin/index.js" },
一旦在全域安裝了@bytepack/cli,就可以直接透過bytepack來執行對應的命令,例如
bytepack -v //显示1.11.0
如果非全域安裝,那麼會自動連接到專案的node_module/.bin目錄中。與前面介紹的script標籤所說的一致,可以直接用別名來使用。
2.3 workspaces
在專案過大的時候,最近又越來越流行monorepo。提到monorepo就繞不看workspaces,早期我們會用yarn workspaces,現在npm官方也支持了workspaces. workspaces解決了本機檔案系統中如何在一個頂層root package下管理多個子packages的問題,在workspaces聲明目錄下的package會軟鏈到最上層root package的node_modules中。
直接以官網的範例來說明:
{ "name": "my-project", "workspaces": [ "packages/a" ] }
在一個npm套件名稱為my-project的npm套件中,存在workspaces設定的目錄。
. +-- package.json +-- index.js `-- packages +-- a | `-- package.json
並且該最上層的名為my-project的root包,有packages/a子包。此時,我們如果npm install,那麼在root package中node_modules中安裝的npm包a,指向的是本地的package/a.
. +-- node_modules | `-- packages/a -> ../packages/a +-- package-lock.json +-- package.json `-- packages +-- a | `-- package.json
上述的
-- packages/a -> ../packages/a
指的就是從node_modules中a連結到本機npm套件的軟鏈
常见的环境,基本上分为浏览器browser和node环境两大类,接下来我们来看看package.json中,跟环境相关的配置属性。环境的定义可以简单理解如下:
3.1 type
js的模块化规范包含了commonjs、CMD、UMD、AMD和ES module等,最早先在node中支持的仅仅是commonjs字段,但是从node13.2.0开始后,node正式支持了ES module规范,在package.json中可以通过type字段来声明npm包遵循的模块化规范。
//package.json { name: "some package", type: "module"||"commonjs" }
需要注意的是:
不指定type的时候,type的默认值是commonjs,不过建议npm包都指定一下type
当type字段指定值为module则采用ESModule规范
当type字段指定时,目录下的所有.js后缀结尾的文件,都遵循type所指定的模块化规范
除了type可以指定模块化规范外,通过文件的后缀来指定文件所遵循的模块化规范,以.mjs结尾的文件就是使用的ESModule规范,以.cjs结尾的遵循的是commonjs规范
3.2 main & module & browser
除了type外,package.json中还有main,module和browser 3个字段来定义npm包的入口文件。
我们来看一下这3个字段的使用场景,以及同时存在这3个字段时的优先级。我们假设有一个npm包为demo1,
----- dist |-- index.browser.js |-- index.browser.mjs |-- index.js |-- index.mjs
其package.json中同时指定了main,module和browser这3个字段,
"main": "dist/index.js", // main "module": "dist/index.mjs", // module // browser 可定义成和 main/module 字段一一对应的映射对象,也可以直接定义为字符串 "browser": { "./dist/index.js": "./dist/index.browser.js", // browser+cjs "./dist/index.mjs": "./dist/index.browser.mjs" // browser+mjs }, // "browser": "./dist/index.browser.js" // browser
默认构建和使用,比如我们在项目中引用这个npm包:
import demo from 'demo'
通过构建工具构建上述代码后,模块的加载循序为:
browser+mjs > module > browser+cjs > main
这个加载顺序是大部分构建工具默认的加载顺序,比如webapck、esbuild等等。可以通过相应的配置修改这个加载顺序,不过大部分场景,我们还是会遵循默认的加载顺序。
3.3 exports
如果在package.json中定义了exports字段,那么这个字段所定义的内容就是该npm包的真实和全部的导出,优先级会高于main和file等字段。
举例来说:
{ "name": "pkg", "exports": { ".": "./main.mjs", "./foo": "./foo.js" } }
import { something } from "pkg"; // from "pkg/main.mjs"
const { something } = require("pkg/foo"); // require("pkg/foo.js")
从上述的例子来看,exports可以定义不同path的导出。如果存在exports后,以前正常生效的file目录到处会失效,比如require('pkg/package.json'),因为在exports中没有指定,就会报错。
exports还有一个最大的特点,就是条件引用,比如我们可以根据不同的引用方式或者模块化类型,来指定npm包引用不同的入口文件。
// package.json { "name":"pkg", "main": "./main-require.cjs", "exports": { "import": "./main-module.js", "require": "./main-require.cjs" }, "type": "module" }
上述的例子中,如果我们通过
const p = require('pkg')
引用的就是"./main-require.cjs"。
如果通过:
import p from 'pkg'
引用的就是"./main-module.js"
最后需要注意的是 :如果存在exports属性,exports属性不仅优先级高于main,同时也高于module和browser字段。
package.json中跟依赖相关的配置属性包含了dependencies、devDependencies、peerDependencies和peerDependenciesMeta等。
dependencies是项目的依赖,而devDependencies是开发所需要的模块,所以我们可以在开发过程中需要的安装上去,来提高我们的开发效率。这里需要注意的时,在自己的项目中尽量的规范使用,形如webpack、babel等是开发依赖,而不是项目本身的依赖,不要放在dependencies中。
dependencies除了dependencies和devDependencies,本文重点介绍的是peerDependencies和peerDependenciesMeta。
3.1 peerDependencies
peerDependencies是package.json中的依赖项,可以解决核心库被下载多次,以及统一核心库版本的问题。
//package/pkg ----- node_modules |-- npm-a -> 依赖了react,react-dom |-- npm-b -> 依赖了react,react-dom |-- index.js
比如上述的例子中如果子npm包a,b都以来了react和react-dom,此时如果我们在子npm包a,b的package.json中声明了PeerDependicies后,相应的依赖就不会重新安装。
需要注意的有两点:
3.2 peerDependenciesMeta
看到“Meta”就有元数据的意思,这里的peerDependenciesMeta就是详细修饰了peerDependicies,比如在react-redux这个npm包中的package.json中有这么一段:
"peerDependencies": { "react": "^16.8.3 || ^17 || ^18" }, "peerDependenciesMeta": { "react-dom": { "optional": true }, "react-native": { "optional": true } }
这里指定了"react-dom","react-native"在peerDependenciesMeta中,且为可选项,因此如果项目中检测没有安装"react-dom"和"react-native"都不会报错。
值得注意的是,通过peerDependenciesMeta我们确实是取消了限制,但是这里经常存在非A即B的场景,比如上述例子中,我们需要的是“react-dom”和"react-native"需要安装一个,但是实际上通过上述的声明,我们实现不了这种提示。
package.json中也存在很多三方属性,比如tsc中使用的types、构建工具中使用的sideEffects,git中使用的husky,eslint使用的eslintIgnore,这些扩展的配置,针对特定的开发工具是有意义的这里不一一举例。
更多node相关知识,请访问:nodejs 教程!
以上是淺析nodejs專案中package.json的常見屬性的詳細內容。更多資訊請關注PHP中文網其他相關文章!