package.json 使用指南

冬天吃雪糕2022年6月21日
大约 11 分钟

package.json 使用指南

概述

一个node项目的根目录下一般都有一个package.json文件。它记录了项目的配置信息(比如名称、版本、许可证等元数据),并定义项目所依赖的包以及包的版本,使构建可重现,因此更容易与其他开发人员共享。

以下是一个最简单的package.json,包含项目的两项元数据(它们也是package.json文件必须具备的两个字段)nameversion

{
  "name" : "xxx",
  "version" : "0.0.0",
}

下面详细解释package.json文件的部分关键字段。

字段介绍

name

包的名称。

  • 少于214个字符,且不能包含空格,
  • 只能包含小写字母、连字符(-)或下划线(_)

version

包的当前版本。

  • 格式为X.Y.Z(主版本号.次版本号.修订号)
  • 遵循SemVer规范

author

包的作者名称。

可以使用字符串形式

{
  "author": "WingSnow"
}

也可以使用以下格式

{
  "author": {
    "name": "WingSnow",
    "email": "py_wing@qq.com",
    "url": "http://www.wingsnow.cn"
  }
}

private

如果设置为true,可以防止包被意外发布到npm上。

homepage

包的项目主页。

main

指定加载的入口文件。如果包名为foo,那么require("foo")就会返回这个文件的export

默认值是模块根目录下面的index.js

files

指定包作为依赖项被安装时要包含的文件(或目录)。该选项的默认值为[*],即包含所有文件。

也可以使用.npmignore来代替,它与.gitignore类似(但是files选项用于列举要包含的文件,而.npmignore相反,用于列举要排除的文件)。

bin

指定各个内部命令对应的可执行文件的位置。

示例:

{
  "bin": {
    "myapp": "./cli.js"
  }
}

以上代码表明myapp命令对应的可执行文件是bin子目录下的cli.js。当安装模块时,npm会寻找这个文件,并在node_modules/.bin/目录下建立符号链接。在上面的例子中,会建立指向cli.js的符号链接node_modules/.bin/myapp

如果是全局安装则符号链接为/usr/local/bin/myapp

当我们使用scripts字段中的脚本时,node_modules/.bin/目录会临时加入系统的 PATH 变量,因此在运行 npm 时,可以不带路径直接调用这些脚本。

"scripts": {"start": "./node_modules/.bin/myapp"}
// 可以简写成
"scripts": {"start": "myapp"}

repository

指定包仓库所在的位置。当使用npm docs命令时,会在浏览器打开该地址。

示例

{
  "repository": {
    "type": "git",
    "url": "https://github.com/npm/cli.git"
  }
}

对于GitHub、GitHub gist、Bitbucket或者GitLab的仓库,可以使用以下简写

"repository": "github:user/repo"
"repository": "gist:11081aaa281"
"repository": "bitbucket:user/repo"
"repository": "gitlab:user/repo"

script

定义一组可以运行的node脚本。

示例:

{
  "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"
  }
}

可以通过调用npm run XXXXyarn XXXXpnpm XXXX来运行它们,其中XXXX是命令的名称。 例如:npm run dev

scripts字段是package.json中最强大、最常用的字段之一,后文再进一步介绍如何使用。

config

添加使用script脚本时的环境变量。

例如在package.json中有以下内容:

{
  "name": "foo",
  "version": "1.2.5",
  "config": {
    "bar": "Hello World!"
  },
  "scripts": {
    "start" : "node index.js"
  }
}

index.js脚本中可以引用npm_package_config_bar的环境变量。

console.log(process.env.npm_package_config_bar)

这样在执行npm run start时,就可以得到bar的值。 但是在不使用script脚本时,该值为undefined。

实际上,在script脚本中可以通过环境变量获取package.json中的任意值。 例如process.env.npm_package_name返回foo,process.env.npm_package_version返回1.2.5

npm run start
# 输出: Hello World!

node index.js
# 输出: undefined

dependencies

设置项目依赖的包及其版本范围。当使用包管理器(如npmyarn)安装软件包时默认会插入此列表。

版本范围描述遵循SemVer规范

常用版本范围
  • 指定版本:如1.2.2
  • >版本:大于指定版本,如>1.2.2;>=、<、<=同理。可以使用多个以指定版本区间,如>1.2.3 <=1.3.2
  • ~版本:大于等于指定版本,但不改变主版本号和次版本号。如~1.2.3,表示安装1.2.x的最新版本(不低于1.2.3),但是不安装1.3.x
  • ^版本:大于等于指定版本,但不改变主版本号。如^1.2.3,表示安装1.x.x的最新版本(不低于1.2.3),但是不安装2.x.x
  • 1.2.x:同~1.2.0
  • *:任意版本,
  • 版本区间:如1.2.3 - 1.3.2,同>=1.2.3 <=1.3.2
  • latest:安装最新版本

使用npm install安装依赖项时,依赖项会以兼容版本的模式(即^x.y.z)写入到dependencies选项中。

devDependencies

设置项目开发依赖的包及其版本范围。这些包只需要安装在开发环境上,而无需在生产环境中使用。如lint工具、测试工具、将其他语言编译为JavaScript的工具等。

当使用包管理器安装软件包时可以选择插入此列表,例如npm install -D <packagename>

对于项目根目录的package.json文件的devDependencies列表中的包,当使用npm install时会与dependencies列表的包一起安装。当作为其他项目的依赖项被安装,本项目的devDependencies不会被安装。

peerDependencies

设置项目对等依赖的包及其版本范围。当依赖本项目安装时,也必须安装指定的对等依赖。

例如:

{
  "name": "ant-design-vue",
  "version": "3.2.3",
  "peerDependencies": {
    "vue": ">=3.2.0"
  }
}

以上设置可以保障包ant-design-vue只有在宿主环境已经安装了指定版本的vue后才可以正确地安装。

安装后会得到如下的目录结构:

node_modules
├── ant-design-vue@3.2.3
└── vue@3.2.0

在不同的包管理器或者同一包管理器的不同版本中,对peerDependencies的处理不同,例如在npm3 - 6中,peerDependencies不会被自动安装,而会在安装结束后检查本次安装是否正确,如果不正确会给用户打印警告提示;而在npm7之后,peerDependencies会被自动安装。

optionalDependencies

设置项目可选依赖的包及其版本范围。

与普通依赖(dependencies)一样,当本项目作为依赖项被安装时,可选依赖会被自动安装。但是即使可选依赖安装失败,也不会影响本项目的继续安装。

这意味着你要在代码中处理依赖缺失的情况,例如:

try {
  var foo = require('foo')
  var fooVersion = require('foo/package.json').version
} catch (er) {
  foo = null
}
if ( notGoodFooVersion(fooVersion) ) {
  foo = null
}
// .. then later in your program ..
if (foo) {
  foo.doFooThings()
}

注意如果在optionalDependenciesdependencies中包含了对同一个包的不同版本的依赖,前者会覆盖后者。建议通常最好只放在一个地方。

engines

设置本软件包的运行环境(如nodenpm等)的版本。

版本范围描述同样遵循SemVer规范。

创建 pacakage.json

通常使用npm init命令创建pacakage.json

npm会通过一系列问题引导创建具有基本信息的pacakage.json

可以使用npm init -y创建默认的pacakage.json

默认的pacakage.json如下:

默认package.json
{
  "name": "my_package",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

其中部分信息会根据当前目录决定,

name:当前目录名

description:如果当前目录下有README.md,则取README.md的第一行非标题内容作为描述,否则为空

可以为init配置默认选项,例如

npm set init.author.name "WingSnow"
npm set init.author.email "py_wing@qq.com"
npm set init.license "MIT"

package-lock.json

由于在package.json的依赖项中通常只会指定版本范围,因此尝试使用npm install命令在另一台机器上复制项目时,如果依赖包已经发布了补丁版本,那么原始的项目和新初始化的项目实际上是不同的,可能会导致问题。

package-lock.json会固化当前安装的每个软件包的版本。

当不指定包的版本运行 npm install时,npm会检查是否存在package-lock.json文件,如有,则会使用这些确切的版本,而忽略package.json文件;如果没有,则会在安装后根据所安装的版本自动生成package-lock.json文件。

而当指定版本(或版本范围)运行npm installnpm update时,则会根据指定版本(或版本范围)或package.json文件在设置的范围内更新到最新版本,然后更新package-lock.json文件。

npm操作node_modules或者package.json文件时,package-lock.json文件被自动更新。

package-lock.json使用建议

开发系统应用时,建议把package-lock.json文件提交到代码版本仓库,从而保证所有团队开发者以及 CI 环节可以在执行npm install时安装的依赖版本都是一致的。

在开发一个 npm包 时,npm包是需要被其他仓库依赖的,如果锁定了依赖包版本,你的依赖包就不能和其他依赖包共享同一 semver 范围内的依赖包,这样会造成不必要的冗余。所以我们不应该把package-lock.json文件发布出去

npm script 使用指南

npm scriptpackage.json中定义的一组内置脚本和自定义脚本。

{
  // ...
  "scripts": {
    "build": "node build.js"
  }
}

在命令行中使用npm run执行以上脚本

npm run build
# 等价于
node build.js

可以使用不带任何参数的npm run命令查看当前项目的所有npm脚本。

原理

每当执行npm run时,会自动新建一个 shell,在这个 shell 里面执行指定的脚本命令。

提示

执行npm run时使用的 shell 程序可以使用npm config set script-shell设置。

上文所述,npm run新建的这个 shell 会将当前目录的node_modules/.bin子目录加入 PATH 变量,执行结束后,再将PATH变量恢复原样。

这意味着,当前目录的node_modules/.bin子目录里面的所有脚本,都可以直接用脚本名调用,而不必加上路径。

执行顺序

如果在同一个npm script中需要执行多个任务,需要明确它们的执行顺序。

要依次运行(只有前一个任务成功,才执行下一个任务)多个脚本,可以使用&&,例如:

npm run lint && npm run build

要并行运行多个脚本,可以使用&,例如:

npm run watch-js & npm run watch-css

注意

这仅适用于 Unix 环境。 在 Windows 中,它将按顺序运行,使用concurrentlyopen in new window模块代替。

默认值与简写

一般来说,npm script由用户提供。但是,npm 对两个脚本提供了默认值。以下两个脚本不需要定义,就可以直接使用。

"start": "node server.js""install": "node-gyp rebuild"

npm run start的默认值是node server.js,前提是项目根目录下有server.js这个脚本;npm run install的默认值是node-gyp rebuild,前提是项目根目录下有binding.gyp文件。

如果你在package.json定义了自己的start和(或)install脚本,则会覆盖默认值。

除了默认值以外,npm 还提供了一些常用脚本的简写形式

  • npm startnpm run start的简写
  • npm stopnpm run stop的简写
  • npm testnpm tnpm run test的简写
  • npm restartnpm stop --if-present && npm start的简写

--if-present 表示即使脚本名不存在也不会报错

注意

除了 npm 之外,yarn 和 pnpm 的运行脚本命令中run本身就可以忽略。例如pnpm run script1可以简写成pnpm script1(内置的cli命令优先于脚本)。

但是对于restart的简写处理则不太一致。例如pnpm restartpnpm stop && pnpm restart && pnpm start的简写

hook

npm scriptprepost两个钩子(hook)。例如build脚本命令的钩子就是prebuildpostbuild,它们会分别在执行build脚本的前后按顺序执行。

"prebuild": "echo \"prebuild\"",
"build": "echo \"build\"",
"postbuild": "echo \"postbuild\"",

当执行npm run build时,等价于

npm run prebuild && npm run build && npm run postbuild

利用这两个钩子,可以完成一些准备工作和清理工作。

传参

npm script传入参数,要使用--标明。

"lint": "eslint **.js"

向上面的npm run lint命令传入参数,必须写成下面这样。

npm run lint --o output.txt

常用脚本

// 清空目录
"clean": "rm -r dist/*"

//  热更新
// 使用nodemon监控src目录下类型为ts,tsx的文件,热更新启动app.ts
"watch": "nodemon --watch ./src -e ts,tsx --exec ts-node ./src/app.ts"
上次编辑于: 2022/8/23 06:32:29
贡献者: WingSnow