webpack第三方库的引用方式问题

1,153 阅读3分钟

最近看了不少关于用webpack做代码切割和持久缓存的文章,又因为这个被公司的大神虐了很久。 写一点收货,也不枉被虐了一番。

npm/yarn

这种方法网上关于webpack的文章里很常见,就是通过npm/yarn进行安装,然后由webpack直接打成模块放进引用他的chunk里。

当然,库的代码和业务代码混在一起肯定是不行的,webpack提供了CommonsChunkPlugin,dllPlugin等插件(webpack4似乎干掉了CommonsChunkPlugin,取而代之的是splitChunks和。。。)来做代码切割。这里不再赘述具体方法,另外有写一篇小结做详细介绍。

我理解最基本的的结果大概是:

  1. vendor,业务中要用到的第三方库库,可以独立提取出来打到一个chunk。
  2. 在异步chunk中使用到的库可以提取到一个async-vendor中。
  3. 业务代码单独打到一个chunk(当然,如果有必要,业务代码也可以再做一个公共代码的提取)
  4. webpack runtime代码。

总之呢,就是第三方库都被统一塞到了一个或几个文件中,组成了体积比较大的vendor chunk。

这么做的好处是什么呢?

  1. 所有的vendor通过一个或几个http请求就搞定了。
  2. webpack帮我们省了很多工作,比如如何按照正确的顺序线性引用第三方库。
  3. 服务器做gzip的时候,对大尺寸的文件做压缩效果会比小文件好(因为更容易出现重复的字符串等)。
  4. 可能和第一点有点矛盾,如果可以使用http2,那么http通信的消耗大大减小,那么我们应该尽可能的把代码拆散。为此,webpack提供了AggressiveSplittingPlugin将大chunk拆成若干个小尺寸的chunk,粒度可以比externals按库切分还细的多。

坏处呢?

  1. 如果不用AggressiveSplittingPlugin,所有库只打一个或者几个vendor,粒度还是太粗了,用一个库就得加载一整个vendor,也不好做按需加载。 如果用AggressiveSplittingPlugin,又需要兼容不支持http2的浏览器的话,表现可能就比较糟糕了。
  2. 每一次build,即使内容没有变化,这些vendor chunk都会被webpack重新生成一遍,浪费时间。

externals

如果不想通过npm来安装第三方库,webpack很贴心的提供了externals接口来让用户自己引入需要的库。

作法呢,很简单。把业务中用到的第三方库通通external掉,然后再通过script把库代码通过global引入。

externals的好处:

  1. 每一个库就是一个chunk,不常用的库可以拿出来做按需加载。一个库的变动不会影响其他库的缓存命中率。
  2. 因为是手动引入的,webpack每次build都可以跳过这些库,节省不少时间。
  3. 和上面相呼应的,如果可以使用http2,拆的粒度理论上是越细越好。每个库都生成一个chunk显然要比打到一个vendor要好。

坏处:

  1. 因为是手动引入,所以如果库之前存在依赖关系,自己得管理好加载顺序。

总的来说呢,两种方法肯定都是可以实现持久化缓存的,但是从切分粒度上来说,用npm/yarn可以切的很粗糙,也可以很细致。而externals就是一个库一个chunk(我认为这样比较适中)。当然,具体哪个好要看自己的业务需求。 另外一个角度来看,build整个工程相较于库的变更肯定是一个相当高频的操作,更值得优化。