缘由
package.json文件中列出了我们需要的npm包依赖,正常情况下都是直接npm install即可,并没有在意。可是前段时间新来同事发现项目出现问题了。找了好久,都是同样的代码。我的可以正常运行,同事的却不行。 最后发现是公共库的版本问题导致
考虑到package.json文件中包版本中的^符号匹配的范围比较大。参考npm官方给出的解释:
^1.2.3 := >=1.2.3 <2.0.0
^0.2.3 := >=0.2.3 <0.3.0
^0.0.3 := >=0.0.3 <0.0.4
于是怀疑是我俩的环境中安装依赖包的小版本不同。于是使用npm ls命令查看:
npm ls --depth 0 --prod
说明
"dependencies": { "jquery": "~2.2.3"} (只是允许小版本的修复变更,即2.2.X)
"dependencies": { "jquery": "^2.2.3"} (允许小版本的变更,即 2.X.X)
"dependencies": { "jquery": "*"} (无限制)
锁定依赖
默认情况下,当用 --save/-S 或者 –save-dev/-D 安装一个模块时,npm 通过脱字符(^)来限定所安装模块的主版本号。
符号 | 当运行 npm update 时,情况说明 | 备注 |
---|---|---|
^1.3.1 | 【限制主版本号】允许安装版本号大于 1.3.1 但小于 2.0.0 版本的模块 | 默认 |
~1.3.1 | 【限制次要版本】允许安装版本号大于 1.3.1 但小于 1.4.0 版本的模块 | npm config set save-prefix="~" |
1.3.1 | 【精确控制】安装版本 1.3.1 | npm config set save-exact true |
由此可知,我们可以将需要安装的模块版本前缀默认设置成波浪号(~);对于那些偏执的认为任何更新(模块的行为)会破坏系统的人,可以配置npm仅安装精确版本号的模块。
npmjs(save-prefix):https://docs.npmjs.com/misc/config#save-prefix
方式二:shrinkwrap
另一个选择是,可以在项目中使用 shrinkwrap,在开发阶段依赖稳定后,运行如下命令:
npm shrinkwrap
npm shrinkwrap--dev # 将dev-dependencies计算在内
这会生成一个 shrinkwrap.json 文件,该文件包含了你正在使用的模块的指定版本。当运行 npm install时,该文件所指定的模块版本会覆盖 package.json 文件中所指定的版本。
注意问题:
shrinkwrap计算时是根据当前依赖安装的目录结构生成的,如果你不能保证package.json文件定义的依赖与node_modules下已安装的依赖是匹配、无冗余的,建议在执行shrinkwrap命令前清理依赖并重新安装(rm -rf node_modules && npm install)或精简依赖(npm prune)。
默认情况下,shrinkwrap只计算dependencies依赖,而不计算dev-dependencies,如果在生产环境也需要开发依赖或你的依赖分类不清晰,使用–dev参数生成shrinkwrap文件确保不会出问题。
小知识
增加、更新、删除包的步骤如下:
- 安装指定版本包 npm install/uninstall package_name@version --save
- 测试功能,功能正常后,执行 npm shrinkwrap 把依赖写入 shrinkwrap文件
- 在代码库中提交 shrinkwrap / package.json 描述
方式三:yarn
yarn是一个与npm兼容的node包管理器。使用它安装npm包,会自动在项目目录中创建一个yarn.lock文件。该文件包含了当前项目中所安装的依赖包的版本信息。其他人在使用yarn安装项目的依赖包时就可以通过该文件创建一个完全相同的依赖环境。
使用方法如下:
yarn # 现有项目中使用yarn来创建yarn.lock
yarn init # 使用yarn新建一个项目
yarn add xxxx # 安装一个依赖包
yarn remove xxx # 删除一个依赖包
更多命令参考yarn官方文档。除了可以自动帮你锁定依赖包的版本之外,yarn还有一个很棒的特性就是可以缓存已经安装过的包。当再次安装时,直接从本地读取即可,可以极大的提高依赖安装速度。
该方法缺陷:需要团队成员都使用yarn,增加初始的沟通成本。
package-lock.json
对于 node_modules 或 package.json 的任何修改,都会自动生成 package-lock.json。它描述了生成的确切树,以便后续安装能够生成相同的树,而不管中间依赖性更新。
shrinkwrap.json
为防止出现这种潜在问题,npm使用 package-lock.json 或 npm-shrinkwrap.json(如果存在)。这些文件称为包锁或锁文件。
该文件描述了一个精确的、可重现的 node_modules 树。一旦它出现,任何未来的安装将基于此文件的工作,而不是从 package.json 重新计算依赖版本。此外,如果 package-lock.json 和 npm-shrinkwrap.json 都存在于包根目录中,则将忽略 package-lock.json 。
npm install,npm rm,npm update 等都将自动同步现有的锁文件。为了防止发生这种更新,你可以使用–no-save 选项来防止完全保存,或者 –no-shrinkwrap 允许更新 package.json 保留 package-lock.json 或npm-shrinkwrap.json 。
注意:强烈建议库作者不要发布此文件,因为这会阻止最终用户控制传递依赖性更新。
问题补充
npm@5、npm@6 版本对于 requires 机制差异!(下图左侧npm@5,右侧npm@6)
相关社区讨论:https://npm.community/t/package-lock-json-contains-dynamic-version/6080/9
相关ChangeLog说明:https://github.com/npm/cli/blob/latest/CHANGELOG.md#new-features-7