第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个在文件名中带哈希值的文件(每个都有入口):
使用这个配置运行 webpack 会生成下面的输出:
但是这种做法的问题是,每次构建,所有文件的的文件名都会被修改,客户端必须重新下载所有的代码。
并不是我们想要的,是吧?那么我们如何保证客户端总是获取到最新的版本,但不需要下载所有的资源?
为每个文件生成唯一的哈希值
如果文件内容不变,生成的文件名就不变会如何?比如说,依赖库文件以及其它不常变化的依赖之类的东西。
专业建议!
使用 CommonsChunkPlugin 区分你的依赖库和应用代码,显式创建一个依赖库的包,防止更新过多。
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 文件:
因此,每次在生产环境中构建时,我们会得到不同的文件名。类似这样:
为了在 HTML 中引用到正确的文件,我们需要知道构建的一些信息。可以使用一个简单的插件从 webpack 的编译统计中导出这些信息:
也可以使用 https://www.npmjs.com/package/webpack-manifest-plugin 或者 https://www.npmjs.com/package/assets-webpack-plugin 导出 JSON 文件。
在我们的配置下的 webpack-manifest-plugin 的一个输出看起来是:
剩下的就依赖你的服务器设置了,但我相信非常简单。如果你使用 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 文件:
因为我们从入口模块中移除了清单,现在我们要把它提供给 webpack。你也许在上面的例子中注意到了 manifestVariable 选项。这是 webpack 寻找清单 JSON 的全局变量,因此它必须在 HTML 中出现在打包文件的前面。把 JSON 的内容内联进 HTML 很简单。HTML 的 head 部分应该是这样的:
第二个问题是 webpack 如何获取模块:默认地对于同样的依赖集合,模块在包中的顺序不是确定的。意思是:在两次构建之间,模块可能获取到不同的标识符,导致不同的内容,也就有了不同的哈希值。这是出现在 Github 上的 issue,建议使用 OccurenceOrderPlugin 来解决这个问题。
Webpack 2.0 已经修复了此问题,现在是 beta 阶段,如果你已经在使用了,就可以安全地移除 OccurenceOrderPlugin。
最后的 webpack.config.js:
使用了这个配置,依赖包就不会更改哈希值,除非你修改了代码或依赖。下面是两次构建的输出,期间修改了 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 的静态资源持久缓存的主要内容,如果未能解决你的问题,请参考以下文章