版本依赖为什么需要锁定
没有版本锁定的情况下,在执行每次npm i的时候,对应的版本前都有个^符号。也就是未固定版本的依赖如果有了次版本更新或者修订版本更新,会自动安装对应的最新版。 在这种情况下,你再次install时安装的包的版本可能与前次不一样,具体的,你可以到package-lock.json中查看实际的包版本。 例如:A新建了一个项目,生成了上面这份package.json文件,但A安装依赖的时间比较早,此时packageA的最新版本是2.1.0,该版本与代码兼容,没有出现bug。后来B克隆了A的项目,在安装依赖时packageA的最新版本是2.2.0,那么根据语义npm会去安装2.2.0的版本,但2.2.0版本的API可能发生了改动,导致代码出现bug。
这就是package.json会带来的问题,同一份package.json在不同的时间和环境下安装会产生不同的结果。
理论上这个问题是不应该出现的,因为npm作为开源世界的一部分,也遵循一个发布原则:相同大版本号下的新版本应该兼容旧版本。即2.1.0升级到2.2.0时API不应该发生变化。但很多开源库的开发者并没有严格遵守这个发布原则,导致了上面的这个问题。
为了在不同的环境下生成相同的node_modules,引入版本依赖锁定就尤为必要了。
npm包管理原理
在思考解决方案前,首先了解下npm包管理及依赖版本管理的原理。这些都是通过package.json文件实现的 当你使用npm安装一个包(并保存它)或者更新一个包的时候,package.json里就自动添加了一条信息,包括包名和其版本。npm默认安装最新版本,然后在其版本号之前添加一个^符号。比如^1.2.12,它表明最低应使用1.2.12版本。并且在这之上,拥有相同大版本号的任何版本都是OK的。毕竟小版本和bugfix版本不会对使用造成任何影响,所以用任何相同大版本的更高级版本都很安全。
- 符号^:表示主版本固定的情况下,可更新最新版。例如:vuex: “^3.1.3”,3.1.3及其以上的3.x.x都是满足的。
- 符号
:表示次版本固定的情况下,可更新最新版。如:vuex: “3.1.3”,3.1.3及其以上的3.1.x都是满足的。 - 无符号:无符号表示固定版本号,例如:vuex: “3.1.3”,此时一定是安装3.1.3版本。
依赖版本锁定方案
package.json中固定版本
这是最直接的,可以在package.json中写入固定版本号,也就是去掉版本号前面的~或者^,或者安装的时候加上–save-exact参数。但这样只能锁定最外一层的依赖,也就是这个依赖本身的其他依赖版本是不受控制的。所以不太推荐。
npm + package-lock.json
第一次npm i的时候会根据当前node_modules目录生成一个固定版本号的package-lock.json文件,后面如果安装新增的依赖,会自动更新这个文件。但如果需要更新当前某个依赖的版本号并锁定到package-lock.josn中,需要手动修改package.json中对应的版本或者指定依赖的版本号安装:npm i xxx@x.x.x。
npm + npm-shrinkwrap.json
这种方式锁定版本,每次依赖有新增或者版本更新之后,要手动自行npm shrinkwrap来生成或者更新版本锁定文件。
yarn+yarn-lock.json
yarn-lock.json与package-lock.josn原理类似,习惯用yarn命令的可以采取这种方式。
npm ci
npm ci 类似于 npm install,常在用于自动化环境,如测试平台,持续集成和部署。区别是:
- 该项目必须有一个 package-lock.json 或 npm-shrinkwrap.json。
- 如果 package-loc 中的依赖项与 package.json 的依赖项不匹配,npm ci 则将退出并显示错误,而不是更新 package-lock。
- npm ci 只能一次安装整个项目:使用此命令无法添加单个依赖项。
- 如果 node_modules 已经存在,它将在 npm ci 开始安装之前自动删除。
- 它永远不会写入 package.json 或任何包锁:安装基本上是冻结的。
npm ci必须存在package-lock.json且依赖版本和package.json匹配时才会安装依赖,否则报错,如此可强制开发者在持续集成前先在本地解决依赖版本的一致性问题。
yarn –frozen-lockfile
yarn --frozen-lockfile和npm ci类似,需要存在yarn-lock.json文件
总得来说,开发环境建议采用 package-lock.json 或者 yarn-lock.json 方案,但是该文件也很容易受到node版本或者包管理器影响而被“改头换面”不好追踪,因此为了防止这种情况,锁定node版本和包管理器版本也是很有必要的
锁定项目Node版本
通过在 package.json 中指定 engines 字段,可限定项目使用的 node 版本。下面配置仅允许用户使用 14 或者 16的版本。更多的配置可以参考 package.json | npm Docs 、semver
1 | // package.json |
配置之后你会发现,该字段只对 yarn 生效。那如何对 npm 也生效呢?
在项目根目录下的 .npmrc 文件中增加如下配置
1 | // .npmrc |
以上配置完成后,npm install 试试吧,错误的 Node.js 将直接退出
锁定包管理器类型
利用 only-allow 工具包、npm scripts 快速实现锁定。
步骤一: 在项目中 npm install -D only-allow
步骤二: 在 package.json 文件中进行配置 scripts.preinstall , 允许输入的值 only-allow npm、only-allow pnpm、only-allow yarn
1 | // package.json |
以上配置完成后,可以再乱用 (yarn、npm、pnpm) 试试
这里有一点需要注意的就是 npm7 以上的 preinstall 是在 install 之后才运行,只有yarn符合预期。
参考文档
本文链接: http://www.ionluo.cn/blog/posts/56a5120f.html
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!
