package.json详解

1,690 阅读12分钟

概览

本文重点讲解前端项目的package.json文件中,所涉及到的字段含义和它的使用场景。避免一些配置性的错误,提高项目的维护性。 image.png

一、描述信息

1. name

如果项目是需要发版为npm包的,则name字段是必须的,以@开头,默认为不发布。pnpm+monorepo根目录的name不需要,private设置为true。

举个例子。对应的源代码package.json的定义如下:

{  
	"name": "sunrise-table",  
	"version": "1.0.0",  
	"description": "xxx"     
}

如果项目是不需要发版成npm包的,则name字段是可选的,不一定要设置。

name命名规范

  • name字符串长度,必须小于或等于214个字符。
  • 同一作用域内的包,可以用._作为开始字符
  • 不能使用大写字母命名
  • 因为name字段,在下载npm包时,会应用于url中,所以不能带任何不安全的URL字符。

不安全的URL字符

  • 空格" "
  • 大于小于号<>
  • 方括号[]
  • 花括号{}
  • 竖线|
  • 反斜杠\
  • 插入号^
  • 百分比%

私源npm包怎么命名

格式:@[scope]/[name]

举个例子:想要发布个私源是@sunrisecn,包名是pkg-a的包,则name需要命名为:@sunrisecn/pkg-a

前提需要在npm账号中创建组织,组织名称为sunrisecn,项目初始化可以使用npm init —scope=sunrisecn 。

{  
	"name": "@sunrisecn/pkg-a",  
}

安装命令:npm install @sunrisecn/pkg-a

2. version

version字段用于定义版本号。如果项目是为发布npm包,则必须包含此字段。如果是普通的项目,则此字段是可选的。

每次发布的version,必须是唯一的,之前发布的时候没使用过的,格式为:主版本号.次版本号.修订号。 语义化版本的规范参考

3. description

description用于描述当前项目的概况。

如下图所示,发布的npm包,在搜索结果中,可以直接显示description内容,方便使用方直接了解包的功能。

image.png

4. keywords

图中标签在package.json中对应的定义:

"keywords": [  "vue" ]

从上述例子可以很清晰地看出,keywords是标签,用于标记当前项目的重点词汇。同时,可以作为搜索关键词,提供给资源平台使用,进行索引。

5. author

作者信息

	"author": {    
	"name": "sun",    
	"email": "xxx@qq.com",    
	"url": "https://wangxiaokai.vip"
}

如果有兴趣让别人知道你是谁,这个字段是必不可少的。

6. contributors

协作者信息。格式是一个对象数组。对象内容和author一致。

"contributors": [
{   
	 "name": "hanmeimei",   
	 "email": "hanmeimei@qq.com"
},
{    
	"name": "lihua",   
	 "email": "lihua@qq.com"
}
]

7. homepage

项目的官网主页地址:

{
	"homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-core#readme"
}

8. repository

项目的源码地址。开源项目,这个字段很重要。因为有意向的协作者,可以通过字段信息,便捷地进入查看项目源码。

"repository": {
    "type": "git",
    "url": "git+https://github.com/vuejs/core.git",
    "directory": "packages/compiler-core"
  },

image.png

9. bugs

bugs表示项目提交问题的地址,该字段是一个对象,可以添加一个提交问题的地址和反馈的邮箱:

"bugs": {
  "url" : "https://github.com/facebook/react/issues",
  "email" : "xxxxx@xx.com"
}

最常见的bugs就是Github中的issues页面,。

二、依赖配置

安装依赖npm i / yarn add pkg@版本,值对象的每一项为一个键值对,前面是模块名称,后面是对应模块的版本范围。版本号遵循“major.minor.patch”的格式规定(主版本号.次版本号.修补版本号)。

修补版本中的更改表示不会破坏任何内容的错误修复。次要版本的更改表示不会破坏任何内容的新功能。主要版本的更改代表了一个破坏兼容性的大变化。 如果用户不适应主要版本更改,则内容将无法正常工作。

  1. 固定版本:比如 5.3.1,安装时只安装指定版本。

  2. 波浪号:比如 ~5.3.1, 表示安装 5.3.x 的最新版本(不低于5.3.1),但是不安装5.4.x,也就是说安装时不改变大版本号和次要版本号。

  3. 插入号:比如 ˆ5.3.1, ,表示安装 5.x.x 的最新版本(不低于5.3.1),但是不安装 6.x.x,也就是说安装时不改变大版本号。需要注意的是,如果大版本号为 0,则插入号的行为与波浪号相同,这是因为此时处于开发阶段,即使是次要版本号变动,也可能带来程序的不兼容。

  4. latest:安装最新版本。

依赖安装时,--save -S参数表示写入dependencies,--save-dev -D表示写入devDependencies。

1. dependencies

 dependencies字段中声明的是项目的生产环境中所必须的依赖包。当使用 npm 或 yarn 安装npm包时,该npm包会被自动插入到此配置项中

2. devDependencies

 devDependencies中声明的是开发阶段需要的依赖包,如Webpack、Eslint、Babel等,用于辅助开发。**例如,如果发布一个包中devDependencies 为lodash,安装这个包,dev中的依赖不会被安装。**

3. peerDependencies

用于放置当前package中使用的依赖,而且会在引用的项目中会存在的依赖。放在peerDependencies中的依赖不会再当前package中下载,而会使用引用的项目中的依赖。

如果package中放在peerDependencies的依赖,但是项目中不存在依赖,同样会报错。

比如:项目A中存在react,package B是一个基于react的依赖包,package B 就不需要自己安装一份react,因为使用它的项目A必然存在react。

比如,项目依赖 A 模块和 B 模块的 1.0.0 版本,而 A 模块本身又依赖 B 模块的 2.0.0 版本,用 peerDepedencies 指定 A 模块 使用 B 的时候,必须是 2.0.0 版本:

{
  "name": "A",
  "peerDependencies": {
    "B": "2.0.0"
  }
}

注意,从npm 3.0版开始,初始化的时候 peerDependencies不会默认带出。

4. optionalDependencies

如果需要在找不到包或者安装包失败时,npm仍然能够继续运行,则可以将该包放在optionalDependencies对象中,optionalDependencies对象中的包会覆盖dependencies中同名的包,所以只需在一个地方进行设置即可。

需要注意,由于optionalDependencies中的依赖可能并为安装成功,所以一定要做异常处理,否则当获取这个依赖时,如果获取不到就会报错。

5. bundledDependencies

上面的几个依赖相关的配置项都是一个对象,而bundledDependencies配置项是一个数组,数组里可以指定一些模块,这些模块将在这个包发布时被一起打包。

需要注意,这个字段数组中的值必须是在dependencies, devDependencies两个里面声明过的包才行。

6. engines

当我们维护一些旧项目时,可能对npm包的版本或者Node版本有特殊要求,如果不满足条件就可能无法将项目跑起来。为了让项目开箱即用,可以在engines字段中说明具体的版本号:
"engines": {    "node": ">=0.10.3 <15"}

上述代码,告知node版本需要在0.10.315之间,才可以运行当前项目。在不符合条件的环境中运行项目时,控制台会有报错输出。

需要注意,engines只是起一个说明的作用,即使用户安装的版本不符合要求,也不影响依赖包的安装。

三、脚本配置

1. scripts

scripts 是 package.json中内置的脚本入口,是key-value键值对配置,key为可运行的命令,可以通过 npm run 来执行命令。除了运行基本的scripts命令,还可以结合pre和post完成前置和后续操作。先来看一组scripts:

"scripts": {
  "dev": "node index.js",
  "start": "icejs start",
  "build": "icejs build",
  "lint": "npm run eslint && npm run stylelint",
  "eslint": "eslint --cache --ext .js,.jsx,.ts,.tsx ./",
}

这些脚本是命令行应用程序。可以通过调用 npm run XXX 或 yarn XXX 来运行它们,其中 XXX 是命令的名称。 例如:npm run start,启动项目。我们可以为命令使用任何的名称,脚本也可以是任何操作。使用好该字段可以大大的提升开发效率。

2. config

用于添加命令行的环境变量。在server.js脚本就可以引用config字段的值。使用npm start 运行。

"config": { "port": 3000 }

如果运行npm run start,则port字段会映射到npm_package_config_port环境变量中:

console.log(process.env.npm_package_config_port) // 3000

用户可以通过npm config set foo:port 3001 命令来重写port的值。

image.png

、文件&目录

下面来看看package.json中和文件以及目录相关的属性。

1. main

main 字段用来指定加载的入口文件,在 browser 和 Node 环境中都可以使用。如果我们将项目发布为npm包,那么当使用 require 导入npm包时,返回的就是main字段所列出的文件的module.exports 属性。如果不指定该字段,默认是项目根目录下的index.js。如果没找到,就会报错。

该字段的值是一个字符串:

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

2. browser

browser字段可以定义 npm 包在 browser 环境下的入口文件。如果 npm 包只在 web 端使用,并且严禁在 server 端使用,使用 browser 来定义入口文件。

{ 
	"browser": "./src/index.js"
}

3. module

module字段可以定义 npm 包的 ESM 规范的入口文件,browser 环境和 node 环境均可使用。如果 npm 包导出的是 ESM 规范的包,使用 module 来定义入口文件。

{ 
	"module": "./src/index.mjs",
}

需要注意,.js/.cjs文件是使用 commonJS 规范的语法(require('xxx')),.mjs 是用 ESM 规范的语法(import 'xxx')。

上面三个的入口入口文件相关的配置是有差别的,特别是在不同的使用场景下。在Web环境中,如果使用loader加载ESM(ES module),

那么这三个配置的加载顺序是browser→module→main,

如果使用require加载CommonJS模块,则加载的顺序main→module→browser。

Webpack在进行项目构建时,有一个target选项,默认为Web,即构建Web应用。如果需要编译一些同构项目,如node项目,则只需将webpack.config.js的target选项设置为node进行构建即可。如果再Node环境中加载CommonJS模块,或者ESM,则只有main字段有效。

4. exports

exports 字段的优先级高于 mainmodule 字段。具体来说,在 Node.js 模块系统中,使用 require() 方法导入模块时,其查找顺序按以下方式进行:

  1. 如果存在 exports 字段,则 Node.js 将使用该字段定义的导出规则来查找导出内容;
  2. 如果不存在 exports 字段,则 Node.js 将使用 main 字段定义的 CommonJS 规范入口文件来查找导出内容;
  3. 如果也不存在 main 字段,则 Node.js 将使用 module 字段定义的 ES 模块规范入口文件来查找导出内容。
{
    "name": "axios",
    "main": "./dist/index.js",
    "module": "./dist/index.mjs",
    "types": "./dist/index.d.ts",
    "exports": {
        '.': { 
        "types": "./dist/index.d.ts", 
        "import": "./dist/index.js", 
        "require": "./dist/index.cjs" 
        },
        "./abort"
        "types": "./dist/unstyled.d.ts", 
        "import": "./dist/unstyled.mjs",
        "require": "./dist/unstyled.js"
        },
    },
}
import axios from 'axios'
const axios  = require('axios')
// 子项
import abort from 'axios/abort
const abort = require('axios/abort')

在实际开发中,推荐你尽可能地配置 exports 字段,以便更好地控制 npm 包的导出规则,提供更好的兼容性和灵活性

5. bin

bin字段用来指定各个内部命令对应的可执行文件的位置:

{
	"bin": {   "someTool": "./bin/someTool.js" }
}

这里,someTool 命令对应的可执行文件为 bin 目录下的 someTool.js。someTool.js会建立符号链接 node_modules/.bin/someTool。

由于node_modules/.bin/目录会在运行时加入系统的PATH变量,因此在运行npm时,就可以不带路径,直接通过命令来调用这些脚本。因此,下面的写法可以简写:

scripts: {
  start: './node_modules/bin/someTool.js build'
}
// 简写
scripts:
{ start: 'someTool build' }

所有node_modules/.bin/目录下的命令,都可以用npm run [命令]的格式运行。

上面的配置在package.json包中提供了一个映射到本地文件名的bin字段,之后npm包将链接这个文件到prefix/fix里面,以便全局引入。或者链接到本地的node_modules/.bin/文件中,以便在本项目中使用。

6. files

files配置是一个数组,用来描述当把npm包作为依赖包安装时需要说明的文件列表。当npm包发布时,files指定的文件会被推送到npm服务器中,如果指定的是文件夹,那么该文件夹下面所有的文件都会被提交。

{
	"files": [ "LICENSE",  "Readme.md",  "index.js",  "lib/"  ]
}

如果有不想提交的文件,可以在项目根目录中新建一个.npmignore文件,并在其中说明不需要提交的文件,防止垃圾文件推送到npm上。这个文件的形式和 .gitignore类似。写在这个文件中的文件即便被写在files属性里也会被排除在外。比如可以在该文件中这样写:

# dependencies
/node_modules

# production
/build
/dist
/.ice

# misc
.idea/
.happypack
.DS_Store

npm-debug.log*
yarn-debug.log*
yarn-error.log*

7. man

man 命令是 Linux 中的帮助指令,通过该指令可以查看 Linux 中的指令帮助、配置文件帮助和编程帮助等信息。如果 node.js 模块是一个全局的命令行工具,在 package.json 通过 man 属性可以指定 man 命令查找的文档地址:

"man": [
  "./man/npm-access.1",
  "./man/npm-audit.1"
]

man 字段可以指定一个或多个文件, 当执行man {包名}时, 会展现给用户文档内容。

8. directories

directories字段用来规范项目的目录。node.js 模块是基于 CommonJS 模块化规范实现的,需要严格遵循 CommonJS 规范。模块目录下除了必须包含包项目描述文件 package.json 以外,还需要包含以下目录:

  • bin :存放可执行二进制文件的目录
  • lib :存放js代码的目录
  • doc :存放文档的目录
  • test :存放单元测试用例代码的目录
  • ...

9. unpkg 、jsdelivr

unpkg 是一个 CDN 服务,可以帮助开发者将自己的 npm 包发布到云端,供其他人直接通过浏览器访问和使用。 jsdelivr 都是为了cdn服务配置 ,另外一个服务

五、发布配置

下面来看看和npm项目包发布相关的配置。

1. private

private字段可以防止我们意外地将私有库发布到npm服务器。只需要将该字段设置为true:

{
	"private": true
}

2. preferGlobal

preferGlobal字段表示当用户不把该模块安装为全局模块时,如果设置为true就会显示警告。它并不会真正的防止用户进行局部的安装,只是对用户进行提示,防止产生误解:

{
	"preferGlobal": true
}

3. publishConfig

publishConfig配置会在模块发布时生效,用于设置发布时一些配置项的集合。如果不想模块被默认标记为最新,或者不想发布到公共仓库,可以在这里配置tag或仓库地址。更详细的配置可以参考 npm-config

通常情况下,publishConfig会配合private来使用,如果只想让模块发布到特定npm仓库,就可以这样来配置:

"private": true,
"publishConfig": {
  "tag": "1.1.0",
  "registry": "https://registry.npmjs.org/",
  "access": "public" 
}

registry 发布的npm私源地址

access 发布有作用域的包(比如@leon/ping-url),必须要设置access

tag 指定当前版本对应的标签

image.png

如图所示,右侧即是标签tag。注意如果没有显式指定tag,默认tag是latest

4. os

os字段可以让我们设置该npm包可以在什么操作系统使用,不能再什么操作系统使用。如果我们希望开发的npm包只运行在linux,为了避免出现不必要的异常,建议使用Windows系统的用户不要安装它,这时就可以使用os配置:

"os" ["linux"]   // 适用的操作系统
"os" ["!win32"]  // 禁用的操作系统

5. cpu

该配置和OS配置类似,用CPU可以更准确的限制用户的安装环境:

"cpu" ["x64", "AMD64"]   // 适用的cpu "cpu" ["!arm", "!mips"]  // 禁用的cpu

可以看到,黑名单和白名单的区别就是,黑名单在前面加了一个“!”。

6. license

license 字段用于指定软件的开源协议,开源协议表述了其他人获得代码后拥有的权利,可以对代码进行何种操作,何种操作又是被禁止的。常见的协议如下:

  • MIT :只要用户在项目副本中包含了版权声明和许可声明,他们就可以拿你的代码做任何想做的事情,你也无需承担任何责任。
  • Apache :类似于 MIT ,同时还包含了贡献者向用户提供专利授权相关的条款。
  • GPL :修改项目代码的用户再次分发源码或二进制代码时,必须公布他的相关修改。

可以这样来声明该字段:

"license": "MIT"

具体的可选协议列表,可查看SPDX License List

六、第三方配置

package.json 文件还可以承载命令特有的配置,例如 Babel、ESLint 等。它们每个都有特有的属性,例如 eslintConfig、babel 等。 它们是命令特有的,可以在相应的命令/项目文档中找到如何使用它们。下面来看几个常用的第三方配置项。

1. typings

项目如果是用TypeScript写的,则需要types字段,对外暴露相关的类型定义。早期的使用typings定义,现在使用types。

"typings": "types/index.d.ts",

该字段的作用和main配置相同。

2. eslintConfig

eslint的配置可以写在单独的配置文件.eslintrc.json 中,也可以写在package.json文件的eslintConfig配置项中。

"eslintConfig": {
  "root": true,
  "env": {"node": true},
  "extends": [
    "plugin:vue/essential",
    "eslint:recommended"
  ],
   "rules": {},
   "parserOptions": {
     "parser": "babel-eslint"
   },
}

3. babel

babel用来指定Babel的编译配置,代码如下:

"babel": {
  "presets": ["@babel/preset-env"],
  "plugins": [...]
}

4. unpkg

CDN方式下,引入当前npm包的链接。unpkg 是一个内容源自 npm 的全球快速 CDN。配置unpkg 字段后,发布到 npmjs.com 中的包会自动同步到 unpkg.com 上,一般为 umd 格式。

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

5. lint-staged

lint-staged是一个在Git暂存文件上运行linters的工具,配置后每次修改一个文件即可给所有文件执行一次lint检查,通常配合gitHooks一起使用。

"lint-staged": {
  "*.js": [
    "eslint --fix",
    "git add"
  ]
}

使用lint-staged时,每次提交代码只会检查当前改动的文件。

6. gitHooks

gitHooks用来定义一个钩子,在提交(commit)之前执行ESlint检查。在执行lint命令后,会自动修复暂存区的文件。修复之后的文件并不会存储在暂存区,所以需要用git add命令将修复后的文件重新加入暂存区。在执行pre-commit命令之后,如果没有错误,就会执行git commit命令:

"gitHooks": { 	"pre-commit": "lint-staged" }

这里就是配合上面的lint-staged来进行代码的检查操作。

7. browserslist

browserslist字段用来告知支持哪些浏览器及版本。Babel、Autoprefixer 和其他工具会用到它,以将所需的 polyfill 和 fallback 添加到目标浏览器。比如最上面的例子中的该字段值:

"browserslist": {
  "production": [
    ">0.2%",
    "not dead",
    "not op_mini all"
  ],
   "development": [
     "last 1 chrome version",
     "last 1 firefox version",
     "last 1 safari version"
   ]
}

8. 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 字段来修改。

9. sideEffects

sideEffects格式:boolean | string[]

sideEffects: false用于告知打包工具(webpack),当前项目无副作用,可以使用tree shaking优化。

sideEffects的值,也可以是一个文件路径组成的数组。告知哪些文件无副作用,可以使用tree shaking优化。

"sideEffects": [    "a.js",    "b.js"]

注意点

"import xxx;"语句,只引入未使用,如果声明了sideEffects,则会被tree shaking删除掉。

并且,由于tree shaking只在production模式生效,所以本地开发会一切正常,生产环境很难及时发现这个问题。

当然, 样式文件使用"import xxx;"的方式引入,会进行保留。

10. workspaces

monorepo类型的项目,需要用到workspaces。它可以告知其他工具,当前项目的工作区间在哪里。

{  "name": "workspace-example",  "workspaces": [    "./packages/*"  ]}

参考

npm Docs