减小发布到npm包的体积与避免重复依赖

这两天一直在忙于封装一个vue table组件并发布到npm,记录一下我是如何把npm包的大小从100多kb减小到不足1kb的过程。

背景

这个组件底层依赖于element-ui,使用了其table组件和pagination组件,最终的组件是一个完全通过配置来描述每一列的表格组件。最开始我发布的是打包之后的代码。如果使用的这个组件的项目中没有引入过element-ui组件,那么不会造成任何重复的依赖,直接引用打包后的版本。但是如果项目本身已经引入了完整的element-ui(我们公司使用这个组件的10余个系统均引入了完整的element-ui),那么很明显会造成代码的重复,会使bundle增加90kb(未压缩时)。

我们需要发布经过打包之后的代码吗

如果发布的经过打包后的组件,是没办法避免重复依赖的。如果可以像把源代码直接copy到项目再import引入这样,就可以避免重复的依赖了。这个可以使用package.json中的module字段来完成。

当package.json中存在module字段时,会优先寻找module对应的文件,并使用ES模块规范处理。

我们可以把未经过打包的源代码发布到npm,并把package.json中的module字段指向源代码,这样引入的package就交由项目的构建工具(webpack, babel)来进行处理,因此理论上就可以避免重复依赖了。

使用module字段是否会有副作用

有可能,因为webpack插件可以配置exclude字段,如果项目的webpack配置exclude掉了node_modules,就会产生副作用。比如可能未经babel转码成es5代码(我发布的这个组件目前只会存在这样一种可能的副作用)。

如何解决副作用

  1. babel转码后再发布
  2. 在readme中指出,让用户取消掉babel的exclude

判断重复依赖的机制

到目前为止,还并不能解决重复依赖的问题。。。这是因为重复依赖的判断机制.Node.js中相同模块是否会被加载多次?

nodeJs是根据模块的路径来判断是否为同一依赖的,而大家都知道node.js会从当前模块所在目录的node_module开始找起,如果没找到再会去找上级目录的node_modules,直到根目录为止。那么问题就来了。

我发布的包里面dependencies里包含element-ui,这没问题,我确实依赖了element-ui。那么install包时,会根据我写明的dependencies下载element-ui并放在包的node_modules里面。所以这个包引用的element-ui和项目本身引用的element-ui由于path不同被认为是不同的依赖,于是都被打包进了bundle里面造成了重复依赖。

如何解决

方法1,在项目的babel配置中添加按需引入element-ui的配置,但是这个方法需要修改项目的配置,比较繁琐,我维护的10来个系统需要一个一个去改,太麻烦了。。。。

方法2,很简单,把发布的包的package.json的dependencies的element-ui和Vue删掉(如果你使用webpack打包你的库,可以使用「externals」),这样npm install的时候就不会下载element-ui到包的node_modules,就是往上级目录找,直到项目node_modules里面的element-ui,这样,包引用的element-ui和项目引用的element-ui就是同一个依赖,就不会重复打包这和vue-cli3打包的库不会包含Vue依赖是同一个原理。最终的结果就是最终生产环境打包后的chunk-vender.js仅仅增加了不到1kb。

不过这样也造成了一定的问题,那就是本身不使用element-ui的项目需要手动引入打包之后的发布的文件。不过不使用element-ui组件的项目使用这个表格组件的收益和概率都不高,如果真的要用的话,单独再发布一个完全打包之后的包,也能快速解决问题。

总结

通过这两天的折腾,主要收获有4点
1、发布npm包的流程
2、package.json中的module字段
3、判断重复依赖的机制
4、基于ui组件封装组件时如何避免重复依赖