前端开发者需要知道的 package.json

7,799 阅读11分钟

这是我参与更文挑战的第二天,活动详情查看:更文挑战

在一个 JavaScript 项目中,package.json 是一个必须的文件,它的作用是管理项目中使用到的外部依赖包,同时它也是 NPM 命令的入口文件。

package.json包含描述一个特定程序包唯一的设置所需的所有内容。

如果项目使用工作空间功能,则项目将包含多个package.json文件,因为每个工作空间都是通过其自己的package.json来描述的。

请注意,可以通过initFields字段设置来设置这些字段的默认值。

🔗name

package.json 中最重要的属性是 nameversion 两个属性,这两个属性是必须要有的,否则模块就无法被安装,这两个属性一起形成了一个 npm 模块的唯一标识

name是 package(包)的名称。名称的第一部分(如@scope/是可选的,用作名称空间)。

当我们的包发布到 NPM 网站,其他人才能通过搜索name来安装使用

{
  "name": "@scope/name"
}

name 规范

  1. 最好取简短而语义化的值
  2. 不能以\,.开头
  3. 不能有大写字母/空格/下滑线!
  4. 不能和 NPM 网站中已有的包名字重名!

🔗version

version是 package(包)的版本,通常,它不会对你的项目产生任何影响,

除非它是一个工作空间(workspaces)-其版本必须与指定的范围相匹配,才能选择该工作空间作为解决方案的候选对象。

当我们的包有了新的迭代,并需要发布到 NPM 网站时,必须升级此版本号才能正常发布

{
  "version": "1.2.3"
}

version 规范

version 具体体现为::"x.y.z"

  1. 修复 bug,小改动,增加 z
  2. 增加了新特性,但仍能向后兼容,增加 y
  3. 有很大的改动,无法向后兼容,增加 x

详细内容参考语义化版本 2.0.0

使用 npm version <update_type>自动升级版本号

update_typepatch, minor, major其中之一,分别表示补丁小改大改

npm version patch

npm version minor

npm version major

在 CI(持续集成)的脚本中可以用到此命令

🔗description

一个描述,方便别人了解你的模块作用,搜索的时候也有用。

{
  "description": "An enterprise-class UI design language and React components implementation"
}

🔗keywords

一个字符串数组,方便别人搜索到本模块

{
  "keywords": [
    "ant",
    "component",
    "components",
    "design",
    "framework",
    "frontend",
    "react",
    "react-component",
    "ui"
  ]
}

当我们使用 npm 检索模块时,会对模块中的 description 字段和 keywords 字段进行匹配,写好 package.json 中的 descriptionkeywords 将有利于增加我们模块的曝光率。


🔗homepage

项目主页 url,默认值为/

一般来说,我们打包的静态资源会部署在 CDN 上,为了让我们的应用知道去哪里加载资源,则需要我们设置一个根路径,这时可以通过 package.json 中的 homepage 字段设置应用的根路径。

{
  "homepage": "https://ant.design"
}

🔗bugs

填写一个 bug 提交地址或者一个邮箱,被你的模块坑到的人可以通过这里吐槽,例如:

{
  "bugs": {
    "url": "https://github.com/ant-design/ant-design/issues"
  }
}

🔗license

你应该为你的开源代码模块制定一个开源协议,让用户知道他们有何权限来使用你的模块,以及使用该模块有哪些限制

  • MIT 是最少约束的选择。
  • GPL 是最多约束的。

如果是个人随意作品,建议 MIT 许可。如果是公司或者需要严格保护的开源产品,GPL

license1

license2

{
  "license": "MIT"
}

详细内容参考

🔗 和用户相关的属性: author, contributors

{
  "author": "某某技术公司",
  "contributors": ["小王", "小李", "校长"]
}

🔗 funding

在开源领域,资金是一个长期存在的问题.

funding命令的作用是让维护 npm 的开发人员(为 Node.js 创建包)声明元数据,为有意愿的捐赠者指明捐赠平台。

package.json 文件中添加了一个funding 字段, 可指向在线捐赠服务的 url,如 PatreonOpen CollectiveGitHub SponsorsLicense Zero 或者其他支付网站。

{
  "funding": {
    "type": "opencollective",
    "url": "https://opencollective.com/ant-design"
  }
}

🔗files

描述了将软件包作为依赖项安装时要包括的条目,默认值为[“*”],这意味着它将包括所有文件。

如果需要把打包后的代码也发布到 NPM 仓库

{
  "files": ["dist/**/*", "lib/**/*"]
}

你还可以在包的根目录或子目录中提供.npmignore 文件,以防止某些文件被发布。

.npmignore 文件的工作原理与.gitignore 一样。

如果存在.gitignore 文件,而缺少.npmignore,则将改用.gitignore 的内容。

files字段内容会覆盖.npmignore.gitignore的内容。

🔗main

main 字段是 package.json 中的另一种元数据功能,它可以用来指定加载的入口文件。

假如你的项目是一个 npm 包,当用户安装你的包后,require('my-module') 返回的是 main 字段中所列出文件的 module.exports 属性。

当不指定main 字段时,默认值是模块根目录下面的 index.js 文件。

{
  "main": "lib/index.js"
}

🔗browserslist

指定该模板供浏览器使用的版本。Browserify 这样的浏览器打包工具,通过它就知道该打包那个文件。

{
  "browserslist": [
    "> 0.5%",
    "last 2 versions",
    "Firefox ESR",
    "not dead",
    "IE 11",
    "not IE 10"
  ]
}

🔗bin

用于将某些可执行 Javascript 文件公开给父包的字段。 此处列出的所有条目都可以通过$ PATH 获得。

通俗点理解就是我们全局安装, 我们就可以在命令行中执行这个文件, 本地安装我们可以在当前工程目录的命令行中执行该文件。

"bin": {
  "my-bin": "./dist/my-bin.js",
}

dist/my-bin.js

#!/usr/bin/env node

console.log("cool");

🔗repository

{
  "repository": {
    "type": "git",
    "url": "https://github.com/ant-design/ant-design"
  }
}

🔗scripts

该字段用于列出在运行 yarn run 时将要执行的小型 shell 脚本。

请注意,包含:(冒号)的脚本是项目的全局变量,无论你当前的工作空间如何,都可以调用它们。

最后,请注意,脚本总是相对于最近的工作空间(而不是 cwd)执行。

"scripts": {
  "test": "jest",
  "build:dev": "webpack-cli --config ./webpack.dev.config.js",
  "build:test": "webpack-cli --config ./webpack.test.config.js",
  "build:pro": "webpack-cli --config ./webpack.pro.config.js"
}

NPM 脚本的原理

npm 脚本的原理非常简单。每当执行 npm run,就会自动新建一个 Shell,在这个 Shell 里面执行指定的脚本命令。因此,只要是 Shell(一般是 Bash)可以运行的命令,就可以写在 npm 脚本里面。

比较特别的是,npm run 新建的这个 Shell,会将当前目录的 node_modules/.bin 子目录加入 PATH 变量,执行结束后,再将 PATH 变量恢复原样。

这意味着,当前目录的 node_modules/.bin 子目录里面的所有脚本,都可以直接用脚本名调用,而不必加上路径。比如,当前项目的依赖里面有 Mocha,只要直接写 mocha test 就可以了。

"test": "mocha test"

而不用写成下面这样。

"test": "./node_modules/.bin/mocha test"

由于 npm 脚本的唯一要求就是可以在 Shell 执行,因此它不一定是 Node 脚本,任何可执行文件都可以写在里面。

npm 脚本的退出码,也遵守 Shell 脚本规则。如果退出码不是0npm 就认为这个脚本执行失败。

🔗config

{
  "name": "foo",
  "config": {
    "port": "8080"
  }
}

当执行npm start命令,就会引用 npm_package_config_port 环境变量,

如上面的配置npm start时,就会通过端口8080启动

但是用户可以通过执行例如 npm config set foo:port 8001 来覆盖config配置。

🔗dependencies

应用依赖,或者叫做业务依赖/生产环境依赖,这是我们最常用的依赖包管理对象!它用于指定应用依赖的外部包,这些依赖是应用发布后正常执行时所需要的,但不包含测试时或者本地打包时所使用的包

{
  "dependencies": {
    "react": "^16.9.0",
    "react-dom": "^16.9.0"
  }
}

放置生产环境依赖包的地方,即执行项目的 npm run build 时需要的依赖包。因此不要把开发环境的依赖包放在这里,比如

  • eslint
  • typesctipt
  • webpack-dev-server
  • ……

因为会增加生产环境安装依赖的时间

🔗devDependencies

dependencies字段类似,但这些依赖项仅在本地开发环境中安装,而不会由软件包的使用者(生产环境)安装。

开发环境依赖,仅次于dependencies的使用频率!它的作用和dependencies一样,只不过它里面的包只用于开发环境,不用于生产环境,这些包通常是单元测试或者打包工具等,例如gulp, webpack, moca

{
  "devDependencies": {
    "webpack": "^4.40.2",
    "webpack-cli": "^3.3.9",
    "webpack-dev-server": "^3.8.1"
  }
}

放置开发环境依赖包的地方,即执行项目的 npm start 时需要的依赖包。因此不要把生产环境的依赖包放在这里

🔗peerDependencies

peerDependencies 的目的是提示宿主环境去安装满足插件 peerDependencies 所指定依赖的包,然后在插件 import 或者 require 所依赖的包的时候,永远都是引用宿主环境统一安装的 NPM 包,最终解决插件与所依赖包不一致的问题。

举个例子,ant-design UI 组件库要求宿主环境安装指定的 React 版本。具体可以看它的 package.json配置

{
  "peerDependencies": {
    "react": ">=16.9.0",
    "react-dom": ">=16.9.0"
  }
}

🔗optionalDependencies

除非你依赖于 fsevents 软件包,否则通常不需要此字段。

如果仅在使用特定功能时才需要包,请使用可选的对等依赖项。

{
  "optionalDependencies": {
    "fsevents": "^5.0.0"
  }
}

🔗engines

engines 字段指明了该模块运行的平台,比如 Node 的某个版本或者浏览器

该字段也可以指定适用的 npm 版本。

{
  "engines": {
    "node": ">=12.18.3",
    "npm": ">7.0.0"
  }
}

🔗private

如果为 true,则该程序包被视为私有程序,Yarn/NPM 会在任何情况下均拒绝发布该程序包。这防止私人存储库意外发布

{
  "private": true
}

🔗publishConfig

此字段包含各种设置,仅当从本地来源生成包时才考虑这些设置(通过 yarn pack 或像 yarn npm publish 这样的发布命令之一)。

publishConfig.access定义将程序包发布到 npm 注册表时要使用的程序包访问级别。 有效值是公开的并且是受限制的,但是受限制的通常需要注册付费计划(这取决于你使用的注册表)。

publishConfig.bin如果存在,则在打包打包以将其运送到远程注册表之前,清单中的顶级 bin 字段将被设置为此新值。 这不会修改真正的清单,只会修改存储在 tarball 中的清单。

publishConfig.browserpublishConfig.bin属性的原理相同; 生成工作空间 tarball 时,将使用此值代替顶级浏览器字段。

publishConfig.executableFiles默认情况下,出于可移植性的原因,在 bin 字段中列出的文件之外的文件都不会在结果包归档文件中标记为可执行文件。 executeFiles 字段使你可以声明必须设置了可执行标志(+ x)的其他字段,即使不能通过 bin 字段直接访问它们也是如此。

publishConfig.mainpublishConfig.bin属性相同的原理; 生成工作空间 tarball 时,将使用此值代替顶级“ main”字段。

publishConfig.modulepublishConfig.bin属性相同的原理; 生成工作空间 tarball 时,将使用此值代替顶级“ module”字段。

publishConfig.registry如果存在,当将包推送到远程位置时,将替换配置中定义的任何注册表。

可能的值

{
  "publishConfig": {
    "access": "public",
    "bin": "./build/bin.js",
    "browser": "./build/browser.js",
    "executableFiles": ["./dist/shim.js"],
    "main": "./build/index.js",
    "module": "./build/index.mjs",
    "registry": "https://npm.pkg.github.com"
  }
}

🔗workspaces

工作区是 monorepos 用来将一个大型项目拆分为半独立子项目的一项可选功能,每个子项目都列出了自己的一组依赖关系。 工作区字段是全局模式列表,这些模式与应成为应用程序工作区的所有目录匹配。

{
  "workspaces": ["packages/*"]
}

🔗type

可能的值

  • commonjs(默认值),适用于 Node.js 环境(服务端)
  • module,即 ES Module 语法,适用于浏览器环境(客户端)

无论使用什么值,当使用 PnP 时,Yarn 3+都会生成一个.pnp.cjs 文件。

{
  "type": "commonjs"
}

main 字段通常用于指向 UMD 版本的库/包,一般指定为 webpack/rollup 打包 UMD 版本后的路径

UMD 是什么呢?

  • CommonJS + AMD 的组合(即 CommonJS 的语法 + AMD 的异步加载)
  • 可以用于 AMD/CommonJS 环境
  • UMD 还支持全局变量定义。因此,UMD 模块能够在 客户端和服务器 上工作。

main 字段还可以在发布时通过使用 publishConfig.main 字段来修改。

🔗module

与 ES6 兼容的环境尝试通过其名称访问程序包时将使用的路径。

module 字段用于指向 ES 版本的库/包,一般指定为 webpack/rollup 打包 ES 版本后的路径

{
  "module": "es/index.js"
}

🔗unpkg

unpkg 是一个内容源自 npm 的全球快速 CDN

配置unpkg 字段后,发布到 npmjs.com 中的包会自动同步到 unpkg.com 上,一般为 umd 格式。

{
  "unpkg": "dist/antd.min.js"
}

🔗typings

{
  "typings": "lib/index.d.ts"
}

🔗sideEffects

与 webpack 相关的字段,声明该模块是否包含 sideEffects(副作用),从而可以为 tree-shaking 提供更大的优化空间。

{
  "sideEffects": ["dist/*", "es/**/style/*", "lib/**/style/*", "*.less"]
}

参考文档

  1. yarn 官方文档
  2. NPM 官方文档

最后

本文介绍了 package.json 的多种常见的配置字段及作用,并通过例子加深大家对 package.json 这些字段的理解。

以上内容如有遗漏错误,欢迎留言 ✍️ 指出,一起进步 💪💪💪 如果觉得本文对你有帮助,请留下你宝贵的 👍

20210601205044