webpack & HTTP/2

4,363 阅读4分钟

webpack & HTTP/2

让我们从 HTTP/2 的一个传言开始:

有了 HTTP/2,你就不再需要打包模块了。

HTTP/2 可以多路复用,所有模块都可以并行使用同一个连接,因此多个请求不再需要多余的往返开销。每个模块都可以独立缓存。

很遗憾,现实并不如意。

以前的文章

下面的文章详细解释了相关信息,并且做了一些实验来验证。你可以阅读它们(或者跳过它们,只看总结)。

Forgo JS packaging? Not so fast The traditional advice for web developers is to bundle the JavaScript files used by their webpages into one or (at most…engineering.khanacademy.org

The Right Way to Bundle Your Assets for Faster Sites over HTTP/2 Speed is always a priority in web development. With the introduction of HTTP/2, we can have increased performance for a…medium.com

文章主旨:

  • 相比拼接为一个文件,多个文件传输仍然有 协议开销(protocol overhead)
  • 相比多个小文件,单文件方式对压缩更友好。
  • 相比处理单个大文件,服务器处理多个小文件较慢。

因此我们需要在两者中间取得一个折中。我们将模块分为 n 个包,n 大于 1,小于模块数。改变其中一个模块使其缓存失效,因为相应的包只是整个应用的一部分,其它的包的缓存仍然有效。

更多的包意味着缓存命中率更高,但不利于压缩。

AggressiveSplittingPlugin

webpack 2 为你提供了这样的工具。webpack 内部大多都是这样,将一组模块组装成块(chunk)输出一个文件。我们还有一个优化阶段可以改变这些块(chunk),只是需要一个插件来做这个优化。

插件 AggressiveSplittingPlugin 将原始的块分的更小。你可以指定你想要的块大小。它提高了缓存,但不利于压缩(对 HTTP/1 来说也影响传输时间)。

为了结合相似的模块,它们在分离之前会按照路径的字母顺序排序。通常在同一目录下的文件往往是相关的,从压缩来看也是一样。通过这种排序,它们也就能分离到相同的块中了。

对于 HTTP/2 我们现在有高效的分块方式了。

修改应用

但这还没结束。当应用更新时我们要尽量复用之前创建的块。因此每次 AggressiveSplittingPlugin 都能够找到一个合适的块大小(在限制内),并将块的模块(modules)和哈希(hash)保存到 records 中。

Records 是 webpack 编译过程中编译状态的概念,可以通过 JSON 文件存取。

当再次调用 AggressiveSplittingPlugin,在尝试分离剩余模块之前,它会先尝试从 records恢复块。这就确保已缓存的块能够被复用。

启动和服务(Bootstrapping and Server)

使用这项技术的应用不再输出包含在 HTML 文件中的单独文件,相反,它输出多个需要被加载的块(chunk),应用就能使用多个 script 标签(并行)加载每个块。就像这样:

<script src="1ea296932eacbe248905.js"></script>
<script src="0b3a074667143853404c.js"></script>
<script src="0dd8c061aff2a2791815.js"></script>
<script src="191b812fa5f7504151f7.js"></script>
<script src="08702f45497539ef6ea6.js"></script>
<script src="195c9326275620b0e9c2.js"></script>
<script src="19817b3a0378aedb2143.js"></script>
<script src="0e7a65e649387d773247.js"></script>
<script src="13167c9702de79d2f4fd.js"></script>
<script src="1154be40ff0e8dd16e9f.js"></script>
<script src="129ce3c198a25d9ace74.js"></script>
<script src="032d1fc9a213dfaf2c79.js"></script>
<script src="07df084bbafc95c1df47.js"></script>
<script src="15c45a570bb174ae448e.js"></script>
<script src="02099ada43bbf02a9f73.js"></script>
<script src="17bc99aaed6b9a23da78.js"></script>
<script src="02d127598b1c99dcd2d0.js"></script>

webpack按时间先后顺序输出这些块。最旧的文件先执行,最新的在最后。浏览器可以先执行已被缓存的块,同时加载最新的文件 -- 旧文件更可能已经被缓存。

当 HTML 文件被请求时,HTTP/2 服务端推送可以将这些块推送给客户端。也是因为旧文件更可能已经被缓存,最好能先推送最新的文件。如果已经有缓存,客户端可以取消服务端的推送,但这需要一次往返。

webpack 将代码分离用于 按需加载,可以处理并行请求。

结论

webpack 2 为你提供了用于 HTTP/2 的,能改善缓存和传输的工具。不用担心你的技术栈不面向未来了。

注意 AggressiveSplittingPlugin 仍然是实验特性

我对你的使用体验很感兴趣哦~


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOSReact前端后端产品设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏