高级前端
webpack
【Q080】使用 webpack 打包时,如何更好地利用 long term cache

使用 webpack 打包时,如何更好地利用 long term cache

Issue 欢迎在 Gtihub Issue 中回答此问题: Issue 81 (opens in a new tab)

Author 回答者: SageSanyue (opens in a new tab)

查阅了很多前辈的资料,总结大致如下:(最后附上链接) long term cache即“持久性缓存” Use [chunkhash] to add a content-dependent cache-buster to each file. Use compiler stats to get the file names when requiring resources in HTML. Generate the chunk-manifest JSON and inline it into the HTML page before loading resources. Ensure that the entry point chunk containing the bootstrapping code doesn’t change its hash over time for the same set of dependencies.

1.使用 [chunkhash] 为每个文件增加一个内容相关的缓存清道夫; Separate development and production configs and use [name].js for development and [name].[chunkhash].js in production.

2.使用编译统计在 HTML 中获取资源时取得文件名;html-webpack-plugin (opens in a new tab)

3.生成 JSON 格式的模块清单文件,并在 HTML 页面加载资源之前内联进去; To fix that, we should use chunk-manifest-webpack-plugin (opens in a new tab) which will extract that manifest to a separate JSON file.

4.保证包含启动代码的入口块不会对于同样的依赖生成不同的哈希值;(3.x 以前的版本是使用 CommonsChunkPlugin 来做代码分离的——将公共库(vendor)和应用程序代码分离开来,并创建一个显式的vendor chunk以防止它频繁更改。而 webpack 4.x 则是把相关的功能包到了optimization.splitChunks中,直接使用该配置就可以实现代码分离。)

代码示例 (opens in a new tab)版本:"webpack": "^1.10.1" 资料来源: webpack中文文档 (opens in a new tab) Webpack Freestyle 之 Long Term Cache (opens in a new tab) Long-term caching of static assets with Webpack (opens in a new tab) 用 webpack 实现持久化缓存 (opens in a new tab) Webpack 的静态资源持久缓存 (opens in a new tab)

Author 回答者: shfshanyue (opens in a new tab)

@SageSanyue 目前更推荐使用 contenthash,而不是 chunkhash

Author 回答者: shfshanyue (opens in a new tab)

Long Term Cache

使用 webpack 等打包器进行打包时,每个资源都可生成一个带有 hash 的路径。如

  • build/main.071b73.js
  • build/main.94474e.css
  • build/logo.18bac8.png

此处对添加 hash 的资源设置永久缓存,可大幅度提高该网站的缓存能力,从而大幅度提高网站的二次加载性能。

通过在服务器端/网关端对资源设置以下 Response Header,进行强缓存一年时间,称为永久缓存,即 Long Term Cache

Cache-Control: public,max-age=31536000,immutable

而当源文件内容发生变更时,资源的 hash 发生变化,生成新的可永久缓存的资源地址。

因此在实践中,可对打包处理后带有 hash 资源的所有文件设置永久缓存。

如果前端通过 docker/k8s/helm 进行部署,可由团队人员自行在构建 nginx 镜像时进行添加响应头字段。此处可作为前端性能优化的 kpi/okr。

可在浏览器控制台 Network 中查看响应头来验证所属项目是否已成功添加永久缓存。

一个问题与更强的永久缓存

假设有两个文件: index.jslib.js,且 index 依赖于 lib,其内容如下。

index.js

// index.js
import("./lib").then((o) => console.log(o));

lib.js

export const a = 3;

由 webpack 等打包器打包后将会生生两个 chunk (为了方便讲解,以下 aaaaaa 为 hash 值)

  • index.aaaaaa.js
  • lib.aaaaaa.js

问: 假设 lib.js 文件内容发生变更,index.js 由于引用了 lib.js,可能包含其文件名,那么它的 hash 是否会发生变动

答: 不一定。打包后的 index.js 中引用 lib 时并不会包含 lib.aaaaaa.js,而是采用 chunkId 的形式,如果 chunkId 是固定的话,则不会发生变更。

// 打包前
import("./lib");
 
// 打包后,201 为固定的 chunkId (chunkIds = deterministic 时)
__webpack_require__.e(/* import() | lib */ 201);

在 webpack 中,通过 optimization.chunkIds 可设置确定的 chunId,来增强 Long Term Cache 能力。

{
  optimization: {
    chunkIds: 'deterministic'
  }
}

设置该选项且 lib.js 内容发生变更后,打包 chunk 如下,仅仅 lib.js 路径发生了变更。

  • index.aaaaaa.js
  • lib.bbbbbb.js