再理解:package.json

1,851 阅读10分钟

前言

作为前端开发,相信大家对package.json并不陌生。但平时在构建业务项目时,对于package.json的关注度其实并不高。大多数情况下都只是打开json文件看一看scripts的指令有哪些,大致浏览一下项目的依赖。那这篇文章的目的就是稍详细地再重温一下package.json文件,其字段的含义,作用,以及注意事项。



正文

理解package.json

package.json文件其实是npm的产物。npm大家并不陌生,其全称是node package manager:是node包管理器。那么package.json文件中的package自然指的就是node package。而package.json就是npm生成的对node包进行描述的文件。

官方对package.json的作用如下所述:

list the packages your project depends on

  • 列出了项目依赖的包

specifies versions of a package that your project can using semantic versioning rules

  • 使用语义化版本控制规则指定了包的版本

makes your build reproducible, and therefore easier to share with other developers

  • 使你的构建具有可复制的特性,便于与其他开发者协作。

总的来说,package.json文件,描述了你的项目、项目依赖、依赖关系、依赖版本,以及带有一些配置信息来实现可复制的开发。

认识package.json字段

package.json文件的字段并不是固定的个数,因为它可以包含来自第三方库、插件的配置字段。也就是说,package.json文件的字段可以分为

  • 官方字段
  • 非官方字段

本文只介绍官方字段,至于非官方字段,依据字段所在的第三方库、插件的api文档自行查阅使用方法即可。

为了便于认识、理解、记忆package.json的字段内容,我把整个官方文档的字段分为三大类:

  • 信息类
  • 配置类
  • 依赖类
  • 其他

如图所示:


package.json字段详解

信息类

nameversion

name and version together form an identifier that is assumed to be completely unique.

名称和版本共同组成了一个唯一的id。(供npm识别)

  • name

平时名称可能起的非常随意,然而公司也肯定有公司的规范,一般来说遵循公司的命名规范不会触及npm规范的底线,然而给项目起名字,确确实实是有规则的:

1. <214个字符
2. 不可以以`.`或者`_`开头(除了域包)
3. 不可以包含大写字母
4. 不可以包含非-URL-安全的字符

这四个条规则是官方文档定义的命名规则,也都很好理解。而name字段也会作为包名、以及require("")的参数出现在相应的位置。

编辑器例如:vscode会对package.json文件的字段进行检查。而vscode对于命名错误时出现的提示为:满足^(?:@[a-z0-9-*~][a-z0-9-*._~]*/)?[a-z0-9-~][a-z0-9-._~]*$该条正则。

事实上该条正则会在你使用了域的情况下以._开头的时候给出警告,但官方是允许这个写法的,所以不管编辑器如何,一切以官方文档为准,同时符合公司规范即可。

version

一个项目版本号,大致由三个部分组成(其实还有标签):

[major].[minor].[patch]

1. major
主要版本号,其递增表示了项目具有类似“模块重构”,“不兼容api的修改”的变动。

2. minor
次要版本号,其递增表示了项目进行了向下兼容的功能性新增。

3. patch
补丁号,其递增表示了项目修复了某些bug,解决了某些小的问题。(当然也必然是向下兼容的)

版本号的变动告知了使用者项目的更新以及迭代的内容,使用者以此来判断是否需要将依赖更新为新的版本。尤其对于组件开发者来说,语义化的版本控制尤为的重要。


keywords, description

这两个字段主要是用来描述你的项目,其目的是为了SEO(Search Engine Optimization —— 搜索引擎优化)

如果你在打算在npm上发布自己的项目,并且希望拥有使用的用户、希望其他开发者可以搜索到你开发的组件。那么就需要好好地填写这两个字段。


license

You should specify a licese for your package so that people know how they are permitted to use it, and any restrictions you're plcing on it.

为你的项目指定一个开源协议。该字段体现了你授权其他开发者使用你的代码的权限,包括不限于:拷贝、修改、使用、出售等。

常见的开源协议有:

不推荐商用

1. GPL
2. LGPL
3. MPL

可商用

1. BSD
2. MIT
3. Apache

最常见的就是MIT协议,名称源自麻省理工大学(Massachusetts Insitute of Technology License)。

被授权人有权利使用、复制、修改、合并、出版发行、散布、再授权及贩售软体及软体的副本;可根据程式的需要修改授权条款为适当的内容;在软件和软件的所有副本中都必须包含版权声明和许可声明。

如果你不想授予任何人任何权限,可以指定UNLICENSED

开源协议的种类非常多,注册在案的可以用过spdx查阅。

同时可以在项目根目录指定一个license文件:SEE LICENSE IN <filename>


author, contrubutors

The "author" is one person, "contributors" is an array of people.

描述作者以及贡献者的信息。

1. name  名称
2. email 邮箱
3. url   相关网站地址

总结构上来看,author包含这三个字段,而contributors为该结构的一个数组。

而这三个字段也可以合并为一个字符串简写:

Your Name <email@example.com> (http://example.com)


homepage, bugs, repository, funding

这四个字段主要内容都是一个网站地址,表达了不同的信息。

1. homepage
即项目主页地址

2. bugs
提交issue的地址,可以是单独的字符,也可以使用对象的结构。
{
  url: string;
  email?: string;
}

3. repository
代码存放的仓库地址
{
  type: string;
  url: stirng;
  directory?: string;
}
其中,directory表示使用了域包时,相对于根目录的路径。

4.funding
捐赠地址。
{
  type: string;
  url: string;
}


配置类

files

The optional files field is an array of file patterns that describes the entries to be included when your package is installed as a dependency.

项目发布的时候包含的文件,可以是带有通配符的表达式。

可能项目发布时你会有很多不希望包含的文件,例如配置文件等。你可以仅仅把需要的文件写入该字段,其他文件会自动忽略。

  1. 默认包含的文件

    • package.json
    • README
    • CHANGES / CHANGELOG / HISTORY
    • LICENCE / LICENSE
    • NOTICE
    • file in "main" field
  2. 默认排除的文件

    • .git
    • .snv
    • .DS_Store
    • .npmrc
    • npm-debug.log
    • node_modules
    • package-lock.json
    • files contain "*" 字符

默认排除的文件其实还有几个,但前端开发会遇到的文件都在这里了。DS_Store是mac文件夹相关的的系统文件。

可以使用.npmIgnore或者.gitIgnore来指定想要排除的文件。

files字段里面所包含的文件,不会被.npmIgnore.gitIgnore排除

如果项目根目录没有.npmIgnore但是有.gitIgnorenpm也会使用.gitIgnore文件


main

The main field is a module ID that is the primary entry point to your program.

项目的主入口文件。

使用者import或者require所导入的模块,便是main字段指定的文件所默认导出的内容。

对于组件开发来说,main指定的文件所导出的模块便是项目的主要模块。


scripts

The "scripts" property is a dictionary containing script command that are run at various times in the lifecycle of your package.

scripts字段主要用来指定、编写脚本。

无论是内置的脚本,还是自定义的脚本,都拥有生命周期:

  • pre
  • post

官方默认脚本

  • publish
  • install / uninstall
  • version
  • test
  • stop
  • start / restart

即运行npm run start的时候,会先后执行:prestart, start, poststart

脚本生命周期的意义在于自动化执行某些操作,例如在postinstall中进行一些文件编译,环境搭建的工作,还可以防止遗漏这些工作。

编写npm脚本的时候需要注意:

  • 不要写npm可以做的事情。
  • 不要在指令中写sudo,获取管理员权限的操作不应该使用脚本完成。
  • 不要书写installpreinstall脚本。

另外可以使用双中划线--来向npm脚本传参。

可以使用&&&符号来继发/并发执行多个npm脚本


bin

a lot of packages have one or more executable files. bin is a map of command name to local file name.

bin用来指定可执行文件以及指令名称。

{
  [commandName]: <local file name>
}

一般来说,可执行文件用于搭建项目的环境,或者快速生成一些模板文件提高效率。

如果只有一个需要执行的文件,并且指令名称就是项目的名称(name),那么可以只用一个文件路径字符来代替bin字段的值。


config, publishConfig

a "config" object can be used to set configuration parameters used in package scripts that persist across upgrades.

总体来说就是npm的配置信息,也可以写在.npmrc文件中,影响npm的行为。

比较需要关注的就以下三个字段:

  • tag 标签,默认是latest,安装依赖时,不指定的情况下就是latest最新版本。

  • registry npm包地址,可以指定为公司的私服地址。

  • access 开放类型,非域包默认为public,域包默认是私有的,设为public也可以公开。

publishConfig即发布时候的配置,完全替代config


依赖类

几种不同的依赖:

dependencies

生产依赖,生产环境下仍需要使用到的依赖。

devDependencies

开发依赖,仅在开发阶段需要使用到的依赖,例如webpack插件,css预处理语言等。

peerDependencies

同辈依赖。用来解决第三方和项目本身的共同依赖版本不同的问题。

npm3.0以前,安装依赖的行为方式有些不同。

例如A,B同时依赖C,那么主项目安装A和B的时候,C依赖会分别成为A和B的子依赖,在A、B目录下node_modules中存在。

而3.0以后npm会将C依赖安装在A和B的同级目录下,这样更好处理依赖关系。

npm3.0以后peerDependencies中的依赖,npm不会主动安装,而是提示宿主环境去手动安装,以此来同步第三方和项目共同使用的依赖的版本。(即都使用宿主环境安装的依赖)

optionalDependencies

可选依赖,当可选依赖安装失败时不会导致错误,npm会继续运行,但需要手动处理错误情况。

可选依赖顾名思义是可有可无的,所以项目必须用到的依赖不可以放在这里。

还有一种使用场景:你的项目同时需要自己启动,以及作为别的项目的依赖,那么两种场景下必然有依赖的差异性。而optinoalDependencies中的依赖可以使用--no--option选项不安装。

bundleDependencies

打包依赖。在npm pack的时候,该字段下的依赖会打包进tgz文件。(使用场景极其有限

版本控制

无论什么类型的依赖,在package.json中,都需要指定他的版本,以此来确保项目所使用的依赖版本都是正确的,不会导致程序崩溃。

而版本控制会通过一些前导符号来指定,具体的细节可以查阅相关网站node-semver

语义化版本控制:

  • ^ 表示大于等于当前版本,并允许minor级别的递增。

  • ~ 表示大于等于当前版本,并允许patch级别的递增。

  • > / >= 表示大于/大于等于当前版本即可。

  • < / <= 表示小于/小于等于当前版本即可。

  • - 表示两个版本范围之间,并包括两个版本。(闭区间)

  • x 可以在major, minor, patch任意位置,表示任何数字。

  • || 表示或,任意版本或版本范围满足其一。

  • 预发布标签 alpha, beta, release, rc等。

需要注意的是,主版本为0版本号规则与其他稍有不同

通常在组件开发期间,以0.1.0作为初始版本号,任何major级别的修改,均递增第二位,minor位置的数字。直到整个项目有一个稳定可公开的版本时,将版本重置为1.0.0,随后按照正常规则进行迭代即可。

所以0开头的版本在版本控制中,~表示主要版本的迭代,包含不可兼容的修改。


others

其他字段都是一些并不常用,只需要了解,不需要过于关注的字段。使用场景非常有限,即便真的遇到了需要使用的场景,查阅npm文档即可。

  • private

防止意外发布的手动,设置为true,npm会阻止发布。

  • engines

可以用来指定nodenpm的版本。

  • borwser

面向客户端的项目可以指定这个字段来代替main

  • directories

描述项目的目录结构,功能仍未完善。

  • man
  • os
  • cpu


后记

所有的官方字段都已经在上面做了详细地介绍,包括一些使用场景和注意事项。(已去除官方文档设为deprecated字段)对于npm官方字段来说,清楚地了解以上这些字段的含义和作用后,也可以一定程度上在出现一些问题的情况下,拥有迅速解决问题的能力。同时也能够更好的理解我们前端开发所离不开的工具 —— node package manager的行为。

更多的配置信息,CLI的相关api,皆可以再官方文档中查询到。npm的文档也进行了更新,可读性比以前更加好。阅读官方文档是一个好的习惯,同时也有助于提升英语阅读的能力。