package和package-lock关于版本管理的那些事
今天本来是个好日子的,但由于昨天发版导致了线上问题,今天就要加紧修复bug了。代码都写好了,可是devops发版却老是报错。前端同事整个人都懵了,一直找不到原因,一般devops也是我在帮忙看。就配合同事梳理了一下原因。
今天发版一直报一下错误:
1 | ERROR Failed to compile with 1 errors |
使用npm ls深入分析
1 | npm ls async-generator-function --depth=10 |
发现完整的依赖链:
1 | webpack-dev-server@2.9.7 |
我看版本和引用的有些差异,我就问同事,你们今天有升级过依赖包吗,同事说,已经很久没变动过了。这些就奇怪了,为什么没动过,却突然报错了呢?经过分析,在本地编译不会报错,在服务器上编译就会报错,因为服务器上每次重新下载依赖包。
问题现象
当重新下载依赖包时,即使package.json
中的版本号没有变化,实际安装的依赖版本可能会发生变化,导致项目运行异常。
原因分析
1. 语义化版本控制(SemVer)
1 | "qs": "^6.11.0" |
^
符号表示兼容主版本的最新版本- 实际安装时可能会安装6.11.x系列的最新版本,如6.11.1、6.11.2等
- 如果6.11系列发布了新版本,重新安装时会自动安装最新版本
2. 依赖树结构变化
- npm的依赖解析算法可能在不同时间产生不同的依赖树结构
- 子依赖的版本更新可能影响整个依赖树
3. 包注册表变化
- npm仓库中的包可能被更新或删除
- 维护者可能发布补丁版本修复bug
解决方案
方案1:使用package-lock.json(推荐)
步骤:
- 确保项目根目录存在
package-lock.json
文件 - 在版本控制系统中跟踪该文件(git add)
- 重新安装依赖时使用
npm ci
命令
优点:
- 精确锁定所有依赖的版本号
- 保证团队成员安装完全相同的依赖版本
- 提高安装速度
操作:
1 | # 生成package-lock.json |
方案2:使用精确的版本号
修改package.json:
1 | "qs": "6.11.0" // 移除^符号 |
优点:
- 确保每次都安装完全相同的版本
缺点:
- 无法自动获取补丁更新
- 需要手动更新版本号
方案3:使用npm shrinkwrap
1 | npm shrinkwrap |
生成的文件:
npm-shrinkwrap.json
:锁定所有依赖的精确版本
方案4:配置.npmrc文件
创建.npmrc
文件:
1 | save-exact=true |
一般来说,我们都会一个package-lock.json
文件来精准锁住版本,但是我看到项目中没有这个文件,我就问同事,你们没有吧package-lock.json
提交到Git吗?同事说,一直没有提交过。那这里其实很清晰了,就是因为引用依赖时版本前面是用的^
且package-lock.json
也未提交导致重新下载时,依赖包更新了。
所以现在要解决上面的问题,多重降级
或者 别名替换
这里我们采用的后者
1 | // build/webpack.base.conf.js |
创建兼容模块
1 | // build/empty-module.js |
最后我们再来回顾一下package.json
版本控制策略
1 | "dependencies": { |
注意版本的管理,不然哪一天项目可能突然就启动不起来了。
package和package-lock关于版本管理的那些事