npm版本固化
在项目开发中,我们需要管理复杂的项目依赖。npm包版本的标准基于semver1,它为依赖的升级提供了便利,但也导致了安装依赖的不一致性。出于以下考虑,我们需要版本固化方案锁定依赖的版本:
- 保证不同环境,不同机器下依赖安装的一致性,
- 避免某些依赖的更新引入新的bug。
目前的版本固化方案有npm shrinkwrap,yarn以及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中的dependencies和node_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.json和npm-shrinkwrap.json两个文件5,有几点需要注意:
- 发布npm包时,并不会带上
package-lock.json - 安装或更新npm包时,优先级
npm-shrinkwrap.json>package-lock.json - 执行
npm shrinkwrap时若存在package-lock.json,会将其重命名为npm-shrinkwrap.json并更新
更新依赖
同npm shrinkwrap。注意npm v5会默认 --save。
总结
对一个项目而言,package.json定义了作者期望的依赖及其版本,而npm-shrinkwrap.json、yarn.lock或者package-lock.json则可以视为某次安装后所有的依赖及其版本的快照。
当然,版本固化只是一个技术方案,实际工程中应该权衡后决定是否使用。67
依赖也分为不同的类型,对optionalDependencies,版本固化方案还存在一些问题。89