前言
npm 是跟前端工程师接触最多的包管理工具,但它对我们来说像是一个黑盒子,在安装依赖时候出现问题往往通过删除大法解决,但有没有想过背后的原理是什么?这样做会不会有什么风险?
下面通过解答LucasHC侯策的《前端基础建设与架构 30 讲》提到的问题来解剖 npm install 原理
- 项目依赖出现问题时,删除大法好,即删除
node_modules和lockfiles,再重新install,这样操作是否存在风险? - 把所有依赖都安装到
dependencies中,不区分devDependencies会有问题吗? - 我们的应用依赖了公共库 A 和公共库 B,同时公共库 A 也依赖了公共库 B,那么公共库 B 会被多次安装或重复打包吗?
- 一个项目中,既有人用
npm,也有人用Yarn,这会引发什么问题? package-lock.json的作用是什么?- 我们是否应该提交
lockfiles文件到项目仓库呢?
npm install 依赖安装流程
问题解剖
项目依赖出现问题时,删除node_modules和lockfiles,再重新install,这样是否存在风险?
当项目没有package-lock.json文件时,那将会按照package.json声明进行安装,而package.json会检查依赖是否有更新版本并且在规范范围内,若符合则进行包小版本升级更新,重新生成 package-lock.json。
这意味着最终依赖包版本会发生小版本改变,在提交最新的package-lock.json到应用时,若当前应用其他成员正在引入一个包正好依赖原来的版本,那么这时候就会发生包版本引用异常。
把所有依赖都安装到 dependencies 中,不区分 devDependencies 会有问题吗?
我们知道 dependencies 是生产依赖即构建时会打包到bundle里面的文件例如react、swiper、loash,devDependencies 是开发工具依赖包比如 webpack、babel-loader、eslint;
但并不是只有在 dependencies 中的模块才会被一起打包,而在 devDependencies 中的依赖一定不会被打包。实际上,依赖是否被打包,完全取决于项目里是否被引入了该模块。
当项目作为一个应用,区别生产依赖 dependencies 和 开发依赖完全是出于一种规范的约定,dependencies 和 devDependencies 不会有实际的影响。
如果项目是发布到npm的一个包并且你的项目本身没有这个依赖,那么这个包的package.json中的dependencies中的依赖是会被下载下来到这个包的node_modules文件夹中。这时候把所有依赖都安装到 dependencies 中会出现依赖包冗余问题。
若依赖包被多次依赖是否会被多次安装或重复打包?
例子:我们的应用依赖了公共库 A 和公共库 B,同时公共库 A 也依赖了公共库 B,那么公共库 B 会被多次安装或重复打包吗?
npm v3 之前:
在安装依赖时将依赖放到项目的node_modules文件中;同时如果某个直接依赖 A 还依赖其他模块 B,作为间接依赖,模块 B 将会被下载到 A 的node_modules文件夹中,依此递归执行,最终形成了一颗巨大的依赖模块树。
npm v3 之后:
当 A 依赖的版本与应用依赖的版本不一致时,则 B 会再次安装到A包下,同时 npm 包的安装顺序对于依赖树的影响很大。模块安装顺序可能影响node_modules内的文件数量。
一个项目中,既有人用 npm,也有人用 Yarn,这会引发什么问题?
我们知道 npm 和 Yarn 的锁版不一样,如果混用会导致依赖包未正确同步或者版本冲突问题,
如 Yarn 官方抛出的警告:
warning package-lock.json found. Your project contains lock files generated by tools other than Yarn. It is advised not to mix package managers in order to avoid resolution inconsistencies caused by unsynchronized lock files. To clear this warning, removepackage-lock.json.
package-lock.json 的作用?
- 保持依赖包版本一致性
package.json 通常只会锁定大版本,意味着按照package.json安装依赖版本可能会发生改变,package-lock.json 可以保证依赖安装的一致性
- 加快依赖包安装的速度
package-lock.json 一般还可以显著加速依赖安装时间
因为package-lock.json中已经缓存了每个包的具体版本和下载链接,你不需要再去远程仓库进行查询,即可直接进入文件完整性校验环节,减少了大量网络请求。
我们是否应该提交 lockfiles 文件到项目仓库呢?
从 npm v5版本开始,增加了package-lock.json文件。我们知道package-lock.json文件的作用是锁定依赖安装结构,目的是保证在任意机器上执行npm install都会得到完全相同的node_modules安装结果。
为什么单一的package.json不能确定唯一的依赖树,因为不同版本的npm的安装依赖策略和算法不同;
npm install 将根据 package.json 中的 semver-range version 更新依赖,某些依赖项自上次安装以来,可能已发布了新版本。
因此,保证能够完整准确地还原项目依赖,就是lockfiles出现的原因。
当开发一个给外部用的的库时,不建议提交lock文件
因为库项目一般是被其他项目依赖的,在不使用package-lock.json的情况下,就可以复用主项目已经加载过的包,减少依赖重复和体积。
如果我们开发的库依赖了一个精确版本号的模块,那么提交 lockfiles 到仓库可能会造成同一个依赖不同版本都被下载的情况。如果作为库开发者,真的有使用某个特定版本依赖的需要,一个更好的方式是定义 peerDependencies。
推荐的做法是:把 package-lock.json 一起提交到代码库中,不需要 ignore。但是执行 npm publish 命令,发布一个库的时候,它应该被忽略而不是直接发布出去。
最后
更多详情推荐看《前端基础建设与架构 30 讲》,欢迎留言评论。