npm版本固化

  • npm
  • yarn
  • 版本固化

在项目开发中,我们需要管理复杂的项目依赖。npm包版本的标准基于semver1,它为依赖的升级提供了便利,但也导致了安装依赖的不一致性。出于以下考虑,我们需要版本固化方案锁定依赖的版本:

  1. 保证不同环境,不同机器下依赖安装的一致性,
  2. 避免某些依赖的更新引入新的bug。

目前的版本固化方案有npm shrinkwrapyarn以及npm package-lock

npm shrinkwrap

版本固化

通过执行命令npm shrinkwrap,可以根据node_modules目录下的依赖生成名为npm-shrinkwrap.json的文件。这个文件描述了安装依赖所需要的基本信息,之后执行npm install就可根据这个文件生成同样的node_modules目录。

npm@4.2.0为例:

{
  "name": "test",
  "version": "1.0.0",
  ...
  "dependencies": {
    "path-to-regexp": "^1.7.0"
  },
  "devDependencies": {},
  ...
}

以上的package.json会生成如下的npm-shrinkwrap.json

{
  "name": "test",
  "version": "1.0.0",
  "dependencies": {
    "isarray": {
      "version": "0.0.1",
      "from": "isarray@0.0.1",
      "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
    },
    "path-to-regexp": {
      "version": "1.7.0",
      "from": "path-to-regexp@>=1.7.0 <2.0.0",
      "resolved": "https://registry.npmjs.org/-/path-to-regexp-1.7.0.tgz"
    }
  }
}

当重新安装依赖时,npm install会安装path-to-regexp@1.7.0及其依赖isarray@0.01

值得一提的是npm-shrinkwrap.json中的dependenciesnode_modules下的依赖均为嵌套结构。如有需要可以执行npm dedupe来去除重复的包,使依赖树尽量扁平化。

更新依赖

可以使用npm update更新相应依赖,随后需要手动重新生成npm-shrinkwrap.json

也可以执行npm install <name>@<version> --save安装指定版本依赖,该指令会自动更新npm-shrinkwrap.json

yarn

当使用yarn安装依赖时,会自动生成yarn.lock,这个文件保存了每个依赖的版本号以及checksum。

同样以上一节的package.json为例,生成的yarn.lock如下:

# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1

isarray@0.0.1:
  version "0.0.1"
  resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"

path-to-regexp@^1.7.0:
  version "1.7.0"
  resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d"
  dependencies:
    isarray "0.0.1"

出于团队协作的考虑,yarn.lock中的依赖只有一层,这样可以方便开发者比较差异,解决冲突。但这种扁平化的格式导致仅凭yarn.lock并能得知项目的直接依赖,依赖的嵌套关系需要从package.json推断,因此yarn能够固化依赖的版本,却不能完全还原node_modules中依赖的位置。2

更新依赖

当执行yarn add/upgrade/remove时,yarn.lock会自动更新。也可在package.json中增加resolutions字段手动指定某个包的依赖。3

npm package-lock

如果使用npm v5安装依赖,会自动生成package-lock.json,其实现同npm-shrinkwrap.json相同。之所以用新的文件,是为了便于理解,并能够向后兼容。4

npm v5中,增删了一些package-lock.json文件中的字段,以npm@5.5.1为例,上述例子生成的package-lock.json如下:

{
  "name": "test",
  "version": "1.0.0",
  "lockfileVersion": 1,
  "requires": true,
  "dependencies": {
    "isarray": {
      "version": "0.0.1",
      "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
      "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
    },
    "path-to-regexp": {
      "version": "1.7.0",
      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
      "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
      "requires": {
        "isarray": "0.0.1"
      }
    }
  }
}

关于package-lock.jsonnpm-shrinkwrap.json两个文件5,有几点需要注意:

  1. 发布npm包时,并不会带上package-lock.json
  2. 安装或更新npm包时,优先级npm-shrinkwrap.json > package-lock.json
  3. 执行npm shrinkwrap时若存在package-lock.json,会将其重命名为npm-shrinkwrap.json并更新

更新依赖

同npm shrinkwrap。注意npm v5会默认 --save

总结

对一个项目而言,package.json定义了作者期望的依赖及其版本,而npm-shrinkwrap.jsonyarn.lock或者package-lock.json则可以视为某次安装后所有的依赖及其版本的快照。

当然,版本固化只是一个技术方案,实际工程中应该权衡后决定是否使用。67

依赖也分为不同的类型,对optionalDependencies,版本固化方案还存在一些问题。89

参考链接