Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

在 Webpack 中分离 vendor 与 app #47

Open
lmk123 opened this issue Sep 12, 2016 · 4 comments
Open

在 Webpack 中分离 vendor 与 app #47

lmk123 opened this issue Sep 12, 2016 · 4 comments
Labels

Comments

@lmk123
Copy link
Owner

lmk123 commented Sep 12, 2016

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

  • vendor.js:包含一些第三方依赖,例如 jQueryVue
  • app.js:包含项目代码

然后给这两个文件根据文件内容计算出一个 hash 加在文件名中,并配置一个长达一年的 Cache Control,这样能大大加快应用的访问速度。

因为 vendor.js 的内容基本上很少更新,所以下一次我们更改了 app.js 的内容时,vendor.js 仍然在浏览器的缓存中,那么用户就只需要重新下载 app.js 了。

但为了在 Webpack 中做到这一点,真是费了不少功夫。

为了将项目中的第三方依赖代码抽离出来,官方文档上推荐使用 CommonsChunkPlugin

当我们在项目里实际使用之后,发现一旦更改了 app.js 内的代码,vendor.js 的 hash 也会改变,那么下次上线时,用户仍然需要重新下载 vendor.js 与 app.js——这样就失去了缓存的意义了。

在网上搜索一番后,发现这是 Webpack 的一个 "Bug": webpack/webpack#1315

webpack 内部使用数字作为模块的 id,一旦 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 仍然变了。举例来说,app.js 里使用的 id 为 40 的模块,在 vendor.js 里的模块 id 为 41,这样就导致 app.js 里使用了错误的模块,于是就报了错。

归根到底,这些问题都是由于模块 id 为数字导致的,所以后来我又试了试 NamedModulesPlugin。顾名思义,它能将模块 id 由数字的形式改为字符串(文件的相对路径)的形式。

虽然这个插件能解决我们的问题,但 app.js 的大小相较于以前大了几乎 60%。虽然 gzip 之后只比以前大了 15%,但我仍然觉得这有点不值得。

踩过这么多次坑之后,我发现根本原因在于 vendor.js 与 app.js 紧耦合了,它们之间的模块 id 会相互影响,所以,如果我们单独打包 vendor.js 和 app.js,问题就解决了。一开始我自己写了一个 CombinePlugin 用来单独打包 vendor.js,然后通过全局变量的方式抛给 app.js 使用,后来翻文档时,才发现官方已经提供了这么一个插件:DLLPlugin

可后来我又发现,虽然 DLLPlugin 解决了修改 app.js 时 vendor.js hash 会变的问题,但在 app.js 里异步加载的文件(使用 Webpack 的 code split 功能)的 hash 会因为 app.js 里新增/删除模块而改变。

最后,我使用了 Webpack 作者写给 webpack 2.0 的 HashedModuleIdsPlugin,发现它居然完整的解决了上面遇到的所有问题:

修改 app.js 或者在 app.js 里添加新的模块时,vendor.js 的 hash 不会变,在 app.js 里异步加载的 chunk 的 hash 也不会变。

我立刻将它应用在了我自用的 Webpack 模版中:lmk123/webpack-boilerplate

至此,心中的一块大石头总算落地了。

@liyanlong
Copy link

所以说 HashedModuleIdsPlugin 这个插件只适用于 webpack2.0 而不适用 1.0 的意思吗?

@lmk123
Copy link
Owner Author

lmk123 commented Jan 17, 2017

@liyanlong HashedModuleIdsPlugin 这个插件也适用于 1.0

@liyanlong
Copy link

@lmk123 好的 :)

@amriogit
Copy link

HashedModuleIdsPlugin 这个插件看起来会把 id 变为非数字呢?
webpack 1.x 真的可用的话 md5 内容当 id 不更好吗?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants