之前对resolution认知几乎为零, 遇到了问题才理解了.

再结合之前幽灵依赖的事聊一下npm依赖的问题.

线上bug, 本地没问题

有个项目使用到了code-mirror作为文本编辑器, 在一个新版本发生了”按回车不能换行”的问题.

在生产环境的控制台看不到任何报错, 而在本地功能却没有问题.

遇到本地生产不一致, 就要思考本地与生产的流程有什么不同, 再逐个验证.

我把本地node_modules删除重新安装后, 可以重现生产问题了.

这就是典型的第三方库不讲武德, npm install默认会给兼容小版本的^修饰, 而第三方库在小版本中加入了会使之前代码行为不一致的特性.

以前有个厉害的同事经常指导我锁版本, 现在算是理解了.

那就锁版本, 但本地白屏了

我把所有code-mirror相关的依赖都锁了版本, 并重新安装, 按我的想象, 所有依赖应该会到以前正常运行的状态.

仔细看了报错, 是code-mirror加载插件报了”非法插件”, 因为用instance of判断了是否插件类的实例, 而code-mirror的库互相依赖, 导致加载了不同版本的”插件类”, 导致instance of判否, 再导致白屏.

我认为需要具体描述一下这个过程:

我的项目有2个依赖: A: 1.0.0, B: 1.0.0.

这2个依赖都锁定了版本为”1.0.0”.

B 也把 A 作为了依赖, 并且在package.json中的声明是向后兼容小版本: ^1.0.0.

在npm安装过程中, B的依赖向后兼容, 就安装了 A@1.5.0 的版本.

而我的项目安装了A@1.0.0 的版本, 是我在我的项目中锁定的, 而我并不能修改 B 项目中的package.json.

最后, B 的代码中, 引入了 A@1.5.0Plugin类, 并且实例化.

而在我项目的执行过程中, 判断了 B 代码中的 1.5.0的实例 是否是 A@1.0.0的实例, 他们来自2个文件, 在混淆后甚至变成了不同字母, 那是不可能通过instance of判定了, 所以报错.

问了朋友, 告诉我在自己package.json中添加一个resolution字段, 声明{ A: 1.0.0 }, 这样项目中的A依赖都会安装1.0.0的版本.

其实前公司一直用resolution固定react版本, 因为react如果存在不同的版本, 也会白屏, 而我一直没去稍微深入了解下.

幽灵依赖导致的白屏

然后再重新说一下之前遇到的严重的依赖问题.

同事的一个组件升级了, 我升级了组件版本, 没有本地运行就推代码, 导致测试环境页面白屏了.

公司项目用的monorepo, 比如react, react-redux这样的每个项目都要用的依赖, 在子项目中就不声明了.

同事组件的新版本声明了react-redux的版本, 是比外层声明低的版本.

在代码中有使用forwardRef, 这个api 是react-redux 7 以后才有的.

之前使用了外层的react-redux没有问题, 当自己模块就存在react-redux老版本后, 就使用老版本作为resolve, 于是就报错了.

node的resolve机制是很简单的, 先找自己目录的node_modules, 再递归往父级找, 找到找不到为止.

总结

如果只遇到上述三个问题中的一个, 我们很容易就会得出结论: 比如”要锁版本”, 或者”要写resolution”, 或者”要杜绝幽灵依赖, 自己import的内容一定要声明”.

但结合一起看会发现, 在一些事情上”准确”, “合理” 是需要具体情况去把握的.

比如我盲目杜绝幽灵依赖, 可能导致项目依赖了某个库的多个版本, 导致打包时间与打包体积的大幅上升.

要记着依赖版本相关的一些特性, 清楚自己在做什么, 具体情况具体处理.

首先得记得, 只修改依赖的版本号, 也是可能影响其他代码的, 记得先运行再提交代码 :(

然后总结下上述问题发生的原因, 都很简单, 遇到问题放松分析就可以:

  • 线上本地不同的问题可能是线上npm没缓存导致, 可以本地删除node_modules重新安装来重现.
  • 在锁版本的时候也要同时使用resolution来锁定深层依赖版本.
  • 在利用幽灵依赖的时候有包的安装和升级要检查新依赖. (在npm install后会在命令行提示的)