Webpack构建项目时将vendor与app目录下的文件分开打包

当我们开发一个单页应用时,常见的优化做法是生成两个文件:

  • vendor.js:包含一些第三方依赖,如:Vue 等
  • app.js:包含业务逻辑代码

通常为了方便开发调试,或者是部署到生产服务器时提高应用的访问速度,我们通常会根据文件内容计算出一个hash值,并将改值添加到文件名中,并配置一个长达一年的 Cache Control。选择这样做,主要是因为 vendor.js(webpack打包出来的第三方库的整合包) 的内容基本上很少更新,所以当我们修改了业务代码并重新生成 app.js 时,vendor.js 仍然在浏览器的缓存中,这样一来用户就只需要重新下载 最新版本的 app.js 即可。

Webpack如何实现代码分割呢

为了实现上面描述的需求--将项目中的第三方依赖代码抽离出来。这里查看了官方文档,官方文档上推荐使用 CommonsChunkPlugin 来抽离公共代码块。但是在项目里实际使用中发现,每当我们更改了业务代码并重新进行项目构建生成 app.js 时,vendor.js 的 hash 值也会随之改变,上面提到的缓存策略也就失去了意义。

问题处在哪里

如果我们打开生成的 app.js查看其内容的话,我们会发现里面有很多采用相同模板标记的模块。带着这些信息再次查看 Webpack 文档发现了一个有意思的issue: https://github.com/webpack/webpack/issues/1315 。进一步研究得知 Webpack 内部使用数字作为模块的 id ,这就可以解释为什么我们每次更新业务代码重新构建新的包时 vendor.js hash值为什么会变化的原因了:因为物业代码的变更,进一步会引发 app.js ,vendor.js 内模块 id 的变化,所以导致了这个 ‘bug’ 的发生。

解决问题

发生了什么

通过上面的 issue ,有人推荐使用 webpack-md5-hash 来进行问题解决。既然有人推荐,那么为何不试试呢,实际使用后发现在更改了业务代码并重新打包生成 app.js 的内容后,vendor.js 的 hash 值不再发生变更,问题得到了初步解决。进一步跟进查看一下 webpack-md5-hash 的源码,发现其是根据webpack编译前的文件内容生成hash值,所以才能做到 vendor.js 的 hash 不变。本来以为满心欢喜的找到了解决方案,但是在项目上线后却发现整个网页一片空白,并且控制台里报了一个错误。这是发生了什么,检查之后才知道,虽然 vendor.js 的 hash 值没变,但是 app.js 里的模块 id 在webpack生成新包时发生了改变。举例来说,新生成的 app.js 里使用的 id 为 40 的模块,在之前的打包生成的 vendor.js 里的模块 id 为 30,这样就导致 app.js 里使用了错误的模块,于是就报了错。

将战斗进行到底

通过进一步排查,我们可以总结得出:这些问题都是由于webpack在打包时,默认使用数字做为模块的 id 导致的。既然这样的话,我们继续查找新的解决方案,发现了 NamedModulesPlugin 这个东东。顾名思义,它能将模块 id 由数字的形式改为字符串(文件的相对路径)的形式。试试,问题确实得到了解决!!!但是,这个不是一颗银弹,采用这种方式打包生成的 app.js 的大小相较于以前的打包方式体积增加了进一半,采用 gzip 压缩之后体积也比之前有所增加。

继续优化解决方案,发现根本原因在于 vendor.js 与 app.js 紧耦合了,它们之间的模块 id 会相互影响。如果我们能够单独打包 vendor.js 和 app.js 的话问题就能迎刃而解了。(尊选一个原则:你所遇见的问题,基本别人也遇见了)。既然这样就那就继续询问Google,Stackoverflow,发现官方已经提供一个插件:DLLPlugin。这个插件能够修改业务代码重新生成 app.js 时 vendor.js 的 hash 值随之变化的问题,但在 app.js 里异步加载的文件(使用 Webpack 的 code split 功能)的hash值还是会因为 app.js 里新增/删除模块而改变。 接着优化解决方案,真是血烦呀!发现了 HashedModuleIdsPlugin 插件,这个插件是Webpack作者为webpack 2.0 版本开发的(虽说是真对2.x 版本开发的,但是貌似也适用于1.x版本),发现它居然完整的解决了上面遇到的所有问题。在修改业务代码生成 app.js 或者在 app.js 里添加新的模块,删除旧模块时,vendor.js 的hash值不在随之改变,同时在 app.js 里采用异步方式加载的 chunk 的hash值也不会在发生改变了。

更多的方案参考: https://webpack.js.org/guides/caching/

你可能感兴趣的:(项目构建,webpack,项目打包,Webpack)