第835期Webpack 的静态资源持久缓存

Posted 前端早读课

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第835期Webpack 的静态资源持久缓存相关的知识,希望对你有一定的参考价值。

前言

你现在还在休假吗?早读课节前以web pack漫谈结尾,今年就以web pack开始吧。今日早读文章由众成翻译@yanni4night带来的翻译。


正文从这开始~


Webpack 是一个将你的所有 javascript、CSS 甚至图片这样的静态资源打包的好方法,但是为了在生产环境中更有效地使用生成的资源,应该考虑使用持久缓存。


摘录

使用 webpack 开启静态资源的持久缓存:

  • 使用 [chunkhash] 为每个文件增加一个内容相关的缓存清道夫;

  • 使用编译统计在 html 中获取资源时取得文件名;

  • 生成 JSON 格式的模块清单文件,并在 HTML 页面加载资源之前内联进去;

  • 保证包含启动代码的入口块不会对于同样的依赖生成不同的哈希值;

  • 开始收益!


问题

每次代码需要更新时,服务器必须重新部署,客户端也必须重新下载资源。因为从网络中获取资源会很慢,这显然非常低效。这就是为什么浏览器会缓存静态资源的原因。但是有一个缺陷:如果在部署新的版本中不修改文件名,浏览器会认为它没有更新,就会使用缓存中的版本。


可能告诉浏览器有更新的最简单方式是修改资源的文件名。在 webpack 之前的时代,我们一般在文件名后面追加参数,每次递增:


使用 webpack 就简单多了:每次构建时 webpack 都会生成一个唯一的哈希值,可用于组合到文件名中。下面的配置示例会生成2个在文件名中带哈希值的文件(每个都有入口):

【第835期】Webpack 的静态资源持久缓存


使用这个配置运行 webpack 会生成下面的输出:

【第835期】Webpack 的静态资源持久缓存


但是这种做法的问题是,每次构建,所有文件的的文件名都会被修改,客户端必须重新下载所有的代码。


并不是我们想要的,是吧?那么我们如何保证客户端总是获取到最新的版本,但不需要下载所有的资源?


为每个文件生成唯一的哈希值

如果文件内容不变,生成的文件名就不变会如何?比如说,依赖库文件以及其它不常变化的依赖之类的东西。


专业建议!


使用 CommonsChunkPlugin 区分你的依赖库和应用代码,显式创建一个依赖库的包,防止更新过多。


Webpack 允许根据文件内容生成哈希值。下面是新的配置:

【第835期】Webpack 的静态资源持久缓存


这个配置也会生成两个文件,但是在这个例子中,每个文件会独立地得到唯一的哈希值。

【第835期】Webpack 的静态资源持久缓存


专业建议!


不要在开发环境中使用 [chunkhash],因为它会增加编译时间。区分开发和生产环境的配置,使用 [name].js 应用于开发,使用 [name].[chunkhash].js 用于生产。


由于 Webpack 的一个问题,该生成哈希值的方法并不是确定的。保证哈希值是根据文件内容生成的,请使用 webpack-md5-hash 插件。这里是使用示例:https://github.com/okonet/webpack-long-term-cache-demo/pull/3/files。


根据 webpack 统计获取文件名

在开发模式下,在 HTML 中直接引用 JavaScript 文件:

【第835期】Webpack 的静态资源持久缓存


因此,每次在生产环境中构建时,我们会得到不同的文件名。类似这样:

【第835期】Webpack 的静态资源持久缓存


为了在 HTML 中引用到正确的文件,我们需要知道构建的一些信息。可以使用一个简单的插件从 webpack 的编译统计中导出这些信息:

【第835期】Webpack 的静态资源持久缓存


也可以使用 https://www.npmjs.com/package/webpack-manifest-plugin 或者 https://www.npmjs.com/package/assets-webpack-plugin 导出 JSON 文件。


在我们的配置下的 webpack-manifest-plugin 的一个输出看起来是:

【第835期】Webpack 的静态资源持久缓存


剩下的就依赖你的服务器设置了,但我相信非常简单。如果你使用 Rails,这是一篇最佳指南。或者你的应用不依赖于任何服务端渲染技术,生成一个单独的 index.html 就足够了,那么建议使用下面两个称赞的插件,https://github.com/ampedandwired/html-webpack-plugin 和 https://github.com/szrenwei/inline-manifest-webpack-plugin,它们会显著地简化设置。


你会认为,到此为止了。然而,还没完。


确定性的哈希值

为了减少生成文件的体积,webpack 使用了标识符而不是文件名。在编译期,标识符是生成的,对于于模块的文件名,并放置于叫做 chunk manifest 的 JavaScript 对象中。它(带着一些启动代码)被置于入口模块中,对于被 webpack 打包的代码来说极其关键。


问题与之前相同:当我们更改了代码的任何一部分,即使剩下的文件内容没有被修改,入口也会被更新以放入新的清单。这样反过来也就导致新的哈希值,影响了长期缓存。


为了修复这个问题,我们应该使用 chunk-manifest-webpack-plugin 插件来把清单导出到单独的 JSON 文件中。这是更新后的 webpack.config.js,它会在构建目录下创建 chunk-manifest.json 文件:

【第835期】Webpack 的静态资源持久缓存


因为我们从入口模块中移除了清单,现在我们要把它提供给 webpack。你也许在上面的例子中注意到了 manifestVariable 选项。这是 webpack 寻找清单 JSON 的全局变量,因此它必须在 HTML 中出现在打包文件的前面。把 JSON 的内容内联进 HTML 很简单。HTML 的 head 部分应该是这样的:

【第835期】Webpack 的静态资源持久缓存


第二个问题是 webpack 如何获取模块:默认地对于同样的依赖集合,模块在包中的顺序不是确定的。意思是:在两次构建之间,模块可能获取到不同的标识符,导致不同的内容,也就有了不同的哈希值。这是出现在 Github 上的 issue,建议使用 OccurenceOrderPlugin 来解决这个问题。


Webpack 2.0 已经修复了此问题,现在是 beta 阶段,如果你已经在使用了,就可以安全地移除 OccurenceOrderPlugin。


最后的 webpack.config.js:

【第835期】Webpack 的静态资源持久缓存


使用了这个配置,依赖包就不会更改哈希值,除非你修改了代码或依赖。下面是两次构建的输出,期间修改了 moduleB.js:


注意依赖包有相同的名字,正是我们所需要的!


结论

Webpack 模块化程序很高,有很多优化在默认下都没有开启。Webpack 提供的灵活性是的任何可以想到的设置成为可能,但是要记住长期缓存是一个常用的优化实践,我希望 webpack 的下一个版本能够默认地让这些事情更容易做到。


关于本文

译者:@yanni4night

译文:http://www.zcfy.cc/article/long-term-caching-of-static-assets-with-webpack-1204.html

原文:https://medium.com/@okonetchnikov/long-term-caching-of-static-assets-with-webpack-1ecb139adb95#.pi3t982sz

以上是关于第835期Webpack 的静态资源持久缓存的主要内容,如果未能解决你的问题,请参考以下文章

webpack 持久化缓存实践

第1747期webpack 5 之持久化缓存指南

webpack实现静态资源缓存

webpack作用

webpack+react+babel环境搭建第二步——静态资源

如何在Ionic中为缓存清除添加哈希到图像和其他静态资源?