一文搞懂webpack hash持久化的原理
目录
- 理解 module、chunk 和 bundle
- hash 的分类
- hash
- runtime 和 manifest
- chunkhash
- contentHash
- webpack5
- 如何使用 hash 做缓存呢?
- 静态资源服务器的缓存
- max-age 配合 hash 做使用
- 参考文章
理解 module、chunk 和 bundle
- module 就是我们通过 import 引入的各种模块
- chunk 是 webpack 根据功能拆分出来的模块,包括入口文件, 动态 import,lazy 等的文件以及 splitChunks 拆分出来的代码,chunk 可能包含多个 module
- bundle 就是 webpack 打包之后的各个文件,于 chunk 一般一一对应
hash 的分类
- hash:the hash of the module identifier(根据 module_id 序列的变化而变化)
- chunkHash:the hash of the chunk content(chunkHash,根据每一个 chunk 内容的变化而变化)
- contentHash:the hash of extracted content(根据内容变化而变化)
hash
- compilation
- webpack 的 hash 是根据 compilation 计算出来的,compilation 对象代表某个版本的资源对应的编译进程,当我们的文件发生改变的时候, 进而能够针对改动生产全新的编译文件。compilation 对象包含当前模块资源、待编译文件、有改动的文件和监听依赖的所有信息,如果我们修改某一个文件,那么此时整个项目的 hash 都会改变
- compiler
- compiler 对象代表的是配置完备的 Webpack 环境。 compiler 对象只在 Webpack 启动时构建一次,由 Webpack 组合所有的配置项构建生成,compiler 对象代表的是不变的 webpack 环境,compilation 是针对随时可变的项目文件
- module_id
- webpack 通过给每一个模块一个 module_id 来处理各个模块之间的依赖关系,而默认的 id 命名规则是根据模块引入的顺序赋予一个整数(1,2,3),所以任意的增添或者删除一个模块的依赖,都会对整个的 ID 序列产生影响,最后影响 hash 值,这些模块会被 runtime 和 manifest 和引用到
- 对于图片、字体、PDF 等资源该 hash 还是可以生成一个唯一值的
- 此时我们配置 webpack 的 output 为 hash
// 此时项目的 mode: 'production', entry: { app: [path.resolve(__dirname, '../src/index.js')], }, output: { filename: 'js/[name].[hash].js', hashDigestLength: 7, path: path.resolve(__dirname, '../dist'), publicPath: './', },
- 项目依赖打包情况如下,我们可以看到所有的 hash 的值都是一样的
Asset Size Chunks Chunk Names css/app.2f3933e.css 52 bytes 0 [emitted] [immutable] app css/list.2f3933e.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.2f3933e.css 71.2 KiB 2 [emitted] [immutable] vendors css/vendors.2f3933e.css.gz 7.85 KiB [emitted] index.html 1.33 KiB [emitted] js/app.2f3933e.js 6.63 KiB 0 [emitted] [immutable] app js/list.2f3933e.js 50.9 KiB 1 [emitted] [immutable] list js/list.2f3933e.js.LICENSE 120 bytes [emitted] js/list.2f3933e.js.gz 15 KiB [emitted] js/vendors.2f3933e.js 340 KiB 2 [emitted] [immutable] [big] vendors js/vendors.2f3933e.js.LICENSE 423 bytes [emitted] js/vendors.2f3933e.js.gz 91.8 KiB [emitted] js/work.2f3933e.js 188 bytes 3 [emitted] [immutable] work
- 此时我们配置 webpack 的 output 为 hash
runtime 和 manifest
- webpack 通过 runtime 和 manifest 来管理所有模块的交互
- runtime
- runtime,以及伴随的 manifest 数据,主要是指:在浏览器运行过程中,webpack 用来连接模块化应用程序所需的所有代码。它包含:在模块交互时,连接模块所需的加载和解析逻辑。包括:已经加载到浏览器中的连接模块逻辑,以及尚未加载模块的延迟加载逻辑
- manifest
- 当 compiler 开始执行、解析和映射应用程序时,它会保留所有模块的详细要点。这个数据集合称为 "manifest",当完成打包并发送到浏览器时,runtime 会通过 manifest 来解析和加载模块。无论你选择哪种 模块语法,那些 import 或 require 语句现在都已经转换为 webpack_require 方法,此方法指向模块标识符(module identifier)。通过使用 manifest 中的数据,runtime 将能够检索这些标识符,找出每个标识符背后对应的模块
- runtime 和 manifest 是一个每次打包都可能变化的不稳定的因素,所以他会导致一些问题,比如,我们对整个项目的文章在做一次打包,打包结果如下,我们发现,我们什么也没有改动但是 hash 全部发生了变化,原因就是 runtime 和 manifest 这些所谓的样板文件
Asset Size Chunks Chunk Names css/app.2f3933e.css 52 bytes 0 [emitted] [immutable] app css/list.2f3933e.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.2f3933e.css 71.2 KiB 2 [emitted] [immutable] vendors css/vendors.2f3933e.css.gz 7.85 KiB [emitted] index.html 1.33 KiB [emitted] js/app.2f3933e.js 6.63 KiB 0 [emitted] [immutable] app js/list.2f3933e.js 50.9 KiB 1 [emitted] [immutable] list js/list.2f3933e.js.LICENSE 120 bytes [emitted] js/list.2f3933e.js.gz 15 KiB [emitted] js/vendors.2f3933e.js 340 KiB 2 [emitted] [immutable] [big] vendors js/vendors.2f3933e.js.LICENSE 423 bytes [emitted] js/vendors.2f3933e.js.gz 91.8 KiB [emitted] js/work.2f3933e.js 188 bytes 3 [emitted] [immutable] work
- 如何解决这个问题
- 我们可以把 runtime 和 manifest 提取出来,去掉这两个不稳定因素,然后打包发现 hash 并未改变,但是我们多了一个 mainfest 文件
optimization: { runtimeChunk: { name: 'manifest', }, }
- 再次打包代码,不断的打包 hash 都不会改变
Asset Size Chunks Chunk Names css/app.c870f3f.css 52 bytes 0 [emitted] [immutable] app css/list.c870f3f.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.c870f3f.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.c870f3f.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.c870f3f.js 3.62 KiB 0 [emitted][immutable] app js/list.c870f3f.js 50.9 KiB 1 [emitted][immutable] list js/list.c870f3f.js.LICENSE 120 bytes [emitted] js/list.c870f3f.js.gz 15 KiB [emitted] js/manifest.c870f3f.js 3.07 KiB 2 [emitted][immutable] manifest js/vendors.c870f3f.js 340 KiB 3 [emitted][immutable] [big] vendors js/vendors.c870f3f.js.LICENSE 423 bytes [emitted] js/vendors.c870f3f.js.gz 91.8 KiB [emitted] js/work.c870f3f.js 188 bytes 4 [emitted][immutable] work
chunkhash
- chunk 就是模块。chunkhash 也就是根据模块内容计算出的 hash 值,很显然,hash 并不适合做本地持久化,所以我们使用 chunkhash 此时修改 webpack 的配置
```javascript optimization: webpackBase.optimization, mode: 'production', entry: { app: [path.resolve(__dirname, '../src/index.js')], } ```
- 修改配置之后打包的结果是(CSS 的结果还是一样的,我们稍后处理)
Asset Size Chunks Chunk Names css/app.8b9de76.css 71.3 KiB 0 [emitted] [immutable] app css/app.8b9de76.css.gz 7.88 KiB [emitted] css/vendors.8b9de76.css 1.5 KiB 3 [emitted] [immutable] vendors index.html 1.27 KiB [emitted] js/app.0df5dd7.js 340 KiB 0 [emitted] [immutable] [big] app js/app.0df5dd7.js.LICENSE 423 bytes [emitted] js/app.0df5dd7.js.gz 92.5 KiB [emitted] js/list.111956e.js 2.48 KiB 1 [emitted] [immutable] list js/manifest.aa8eb6d.js 3.13 KiB 2 [emitted] [immutable] manifest js/vendors.49e3e7f.js 48.5 KiB 3 [emitted] [immutable] vendors js/vendors.49e3e7f.js.LICENSE 120 bytes [emitted] js/vendors.49e3e7f.js.gz 13.8 KiB [emitted] js/work.1b2fd82.js 188 bytes 4 [emitted] [immutable] work
- 这个时候修改 list.js,然后继续打包,css 的 hash 变了,正常因为它使用的是 hash 不是 chunkhash,list 的 hash 也变了,正常因为我们修改了这个文件,work 的 hash 并没有变化,完全正常
```javascript Asset Size Chunks Chunk Names css/app.1a93a35.css 71.3 KiB 0 [emitted] [immutable] app css/app.1a93a35.css.gz 7.88 KiB [emitted] css/vendors.1a93a35.css 1.5 KiB 3 [emitted] [immutable] vendors index.html 1.27 KiB [emitted] js/app.0df5dd7.js 340 KiB 0 [emitted] [immutable] [big] app js/app.0df5dd7.js.LICENSE 423 bytes [emitted] js/app.0df5dd7.js.gz 92.5 KiB [emitted] js/list.5b187e3.js 2.48 KiB 1 [emitted] [immutable] list js/manifest.3752b77.js 3.13 KiB 2 [emitted] [immutable] manifest js/vendors.49e3e7f.js 48.5 KiB 3 [emitted] [immutable] vendors js/vendors.49e3e7f.js.LICENSE 120 bytes [emitted] js/vendors.49e3e7f.js.gz 13.8 KiB [emitted] js/work.1b2fd82.js 188 bytes 4 [emitted] [immutable] work ```
- 这个时候我们为 list.js 引入一个新的 js,css 改变我们暂且不论,这个时候发现 vendors.js, app.js, work.js 竟然全部改变了, 这不符合我们的预期,这是因为每个 module.id 会基于默认的解析顺序(resolve order)进行增量(类似于没有指定 key 的 react 的组件的渲染)。也就是说,当解析顺序发生变化,ID 也会随之改变,所以我们需要自己命名这个 moduleid
```javascript Asset Size Chunks Chunk Names css/app.636f1cd.css 52 bytes 0 [emitted] [immutable] app css/list.636f1cd.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.636f1cd.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.636f1cd.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.0b4c163.js 3.62 KiB 0 [emitted][immutable] app js/list.d02fa6a.js 51 KiB 1 [emitted][immutable] list js/list.d02fa6a.js.LICENSE 120 bytes [emitted] js/list.d02fa6a.js.gz 15 KiB [emitted] js/manifest.3a9ff17.js 3.09 KiB 2 [emitted][immutable] manifest js/vendors.a1bfd17.js 340 KiB 3 [emitted][immutable] [big] vendors js/vendors.a1bfd17.js.LICENSE 423 bytes [emitted] js/vendors.a1bfd17.js.gz 91.8 KiB [emitted] js/work.f70d2d8.js 188 bytes 4 [emitted][immutable] work ```
- 我们自己命名这个 ID 把,命名的方式如下
// 将默认的数字 id 命名规则换成路径的方式。webpack 4 中当 mode 为 development 会默认启动 optimization: { namedModules: true } // 但是如果把路径作为ID难免太长,所以我们使用HashedModuleIdsPlugin来生成hash plugins: [ new webpack.HashedModuleIdsPlugin(), ], // 此时进行打包的结果是 Asset Size Chunks Chunk Names css/app.d36a7df.css 52 bytes 0 [emitted] [immutable] app css/list.d36a7df.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.d36a7df.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.d36a7df.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.5aef12b.js 3.74 KiB 0 [emitted] [immutable] app js/list.9dbf1d7.js 51.5 KiB 1 [emitted] [immutable] list js/list.9dbf1d7.js.LICENSE 120 bytes [emitted] js/list.9dbf1d7.js.gz 15.7 KiB [emitted] js/manifest.cf2b1ee.js 3.09 KiB 2 [emitted] [immutable] manifest js/vendors.612571f.js 343 KiB 3 [emitted] [immutable] [big] vendors js/vendors.612571f.js.LICENSE 423 bytes [emitted] js/vendors.612571f.js.gz 97.7 KiB [emitted] js/work.3d8d43d.js 196 bytes 4 [emitted] [immutable] work
- 此时为 list 再次 import 一个文件,打包之后 hash 的值是,此时我们发现 app.js 的值没有变,list 的值改变了,vendors 和 work 都没变完全符合我们的预期,至此 js hash 的过程已经完全结束
```javascript Asset Size Chunks Chunk Names css/app.39db041.css 52 bytes 0 [emitted] [immutable] app css/list.39db041.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.39db041.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.39db041.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.5aef12b.js 3.74 KiB 0 [emitted] [immutable] app js/list.a0c9911.js 51.5 KiB 1 [emitted] [immutable] list js/list.a0c9911.js.LICENSE 120 bytes [emitted] js/list.a0c9911.js.gz 15.7 KiB [emitted] js/manifest.14406a1.js 3.09 KiB 2 [emitted] [immutable] manifest js/vendors.612571f.js 343 KiB 3 [emitted] [immutable] [big] vendors js/vendors.612571f.js.LICENSE 423 bytes [emitted] js/vendors.612571f.js.gz 97.7 KiB [emitted] js/work.3d8d43d.js 196 bytes 4 [emitted] [immutable] work ```
contentHash
- 之前我们还有一个遗留问题,就是 css 的 hash 每次都会产生变化,是因为我们之前配置了抽离的 css 是 hash,根据上面的文章,我们修改为 chunkhash
```javascript // 之前的配置 miniCssExtract: new MiniCssExtractPlugin({ filename: 'css/[name].[hash].css', chunkFilename: 'css/[name].[hash].css', ignoreOrder: false, }); // 修改之后的配置 miniCssExtract: new MiniCssExtractPlugin({ filename: 'css/[name].[chunkhash].css', chunkFilename: 'css/[name].[chunkhash].css', ignoreOrder: false, }); ```
- 修改为 chunkhash 之后,当然 css 的值就不会每次都发生变化了,此时我们对项目进行打包,然后修改 work.js 我们会发现 css 的 hash 并没有发生(此处不在尝试) 任何变化,完全符合我们的预期,但是我们却发现,我们是以 chunk 做 hash,所以导致了一个问题,list.js 和 list.css 的 hash 值一摸一样,因为他们属于同一个 chunk
```javascript Asset Size Chunks Chunk Names css/app.5aef12b.css 52 bytes 0 [emitted] [immutable] app css/list.a0c9911.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.612571f.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.612571f.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.5aef12b.js 3.74 KiB 0 [emitted] [immutable] app js/list.a0c9911.js 51.5 KiB 1 [emitted] [immutable] list js/list.a0c9911.js.LICENSE 120 bytes [emitted] js/list.a0c9911.js.gz 15.7 KiB [emitted] js/manifest.171619f.js 3.12 KiB 2 [emitted] [immutable] manifest js/vendors.612571f.js 343 KiB 3 [emitted] [immutable] [big] vendors js/vendors.612571f.js.LICENSE 423 bytes [emitted] js/vendors.612571f.js.gz 97.7 KiB [emitted] js/work.3d8d43d.js 196 bytes 4 [emitted] [immutable] work ```
- 对 app.css 做修改,然后重新打包,打包结果如下,我们发现,app.css 的 hash 发生了变化,但是 app.js 的 hash 也发生了变化,这就是因为 app.css 和 app.js 属于同一个 chunk,所以这个时候我们就必须对 css 单独处理让他根据自己的 content 去做 hash 而不是 chunk
```javascript Asset Size Chunks Chunk Names css/app.131454e.css 52 bytes 0 [emitted] [immutable] app css/list.a0c9911.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.612571f.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.612571f.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.131454e.js 3.74 KiB 0 [emitted] [immutable] app js/list.a0c9911.js 51.5 KiB 1 [emitted] [immutable] list js/list.a0c9911.js.LICENSE 120 bytes [emitted] js/list.a0c9911.js.gz 15.7 KiB [emitted] js/manifest.171619f.js 3.12 KiB 2 [emitted] [immutable] manifest js/vendors.612571f.js 343 KiB 3 [emitted] [immutable] [big] vendors js/vendors.612571f.js.LICENSE 423 bytes [emitted] js/vendors.612571f.js.gz 97.7 KiB [emitted] js/work.3d8d43d.js 196 bytes 4 [emitted] [immutable] work ```
- 修改配置然后重新打包代码
// 修改配置 miniCssExtract: new MiniCssExtractPlugin({ filename: 'css/[name].[contenthash].css', chunkFilename: 'css/[name].[contenthash].css', ignoreOrder: false, }); /* 重新打包代码如下,可以看到app.js和app.css的hash不一致了 */ Asset Size Chunks Chunk Names css/app.15e0de3.css 52 bytes 0 [emitted] [immutable] app css/list.8298c67.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.353f491.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.353f491.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.131454e.js 3.74 KiB 0 [emitted] [immutable] app js/list.a0c9911.js 51.5 KiB 1 [emitted] [immutable] list js/list.a0c9911.js.LICENSE 120 bytes [emitted] js/list.a0c9911.js.gz 15.7 KiB [emitted] js/manifest.88160aa.js 3.12 KiB 2 [emitted] [immutable] manifest js/vendors.612571f.js 343 KiB 3 [emitted] [immutable] [big] vendors js/vendors.612571f.js.LICENSE 423 bytes [emitted] js/vendors.612571f.js.gz 97.7 KiB [emitted] js/work.3d8d43d.js 196 bytes 4 [emitted] [immutable] work
- 修改 app.css,然后再次打包代码,打包结果如下,我们发现 除了 app.css hash 改变,app.js 的 hash 一样的发生了改变,这又是为什么呢,通过试验是因为 CSS moduley 引起的问题,因为 css 文件的改变也会改变到 js,初步猜测是 css module 的问题,经过试验发现即使去掉 cssMOdule 还是有同样的问题
```javascript Asset Size Chunks Chunk Names css/app.29ae3c7.css 52 bytes 0 [emitted] [immutable] app css/list.8298c67.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.353f491.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.353f491.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.3bafc2a.js 3.74 KiB 0 [emitted] [immutable] app js/list.a0c9911.js 51.5 KiB 1 [emitted] [immutable] list js/list.a0c9911.js.LICENSE 120 bytes [emitted] js/list.a0c9911.js.gz 15.7 KiB [emitted] js/manifest.88160aa.js 3.12 KiB 2 [emitted] [immutable] manifest js/vendors.612571f.js 343 KiB 3 [emitted] [immutable] [big] vendors js/vendors.612571f.js.LICENSE 423 bytes [emitted] js/vendors.612571f.js.gz 97.7 KiB [emitted] js/work.3d8d43d.js 196 bytes 4 [emitted] [immutable] work ```
- 后来一想,其实跟上面的 app.js 和 app.css hash 一样是同样的问题,app.js 的改变,就是会改变 chunk 的值,所以把修改 webpack 的配置如下
Asset Size Chunks Chunk Names css/app.44b7866.css 38 bytes 0 [emitted] [immutable] app css/list.8298c67.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.353f491.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.353f491.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.ca738c5.js 3.67 KiB 0 [emitted] [immutable] app js/list.1895c1a.js 51.5 KiB 1 [emitted] [immutable] list js/list.1895c1a.js.LICENSE 120 bytes [emitted] js/list.1895c1a.js.gz 15.7 KiB [emitted] js/manifest.b7ee988.js 3.12 KiB 2 [emitted] [immutable] manifest js/vendors.38fec86.js 343 KiB 3 [emitted] [immutable] [big] vendors js/vendors.38fec86.js.LICENSE 423 bytes [emitted] js/vendors.38fec86.js.gz 97.7 KiB [emitted] js/work.ea3817c.js 196 bytes 4 [emitted] [immutable] work /* 修改css之后然后再次打包,果然解决了之前的问题 */ Asset Size Chunks Chunk Names css/app.208b221.css 39 bytes 0 [emitted] [immutable] app css/list.8298c67.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.353f491.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.353f491.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.ca738c5.js 3.67 KiB 0 [emitted] [immutable] app js/list.1895c1a.js 51.5 KiB 1 [emitted] [immutable] list js/list.1895c1a.js.LICENSE 120 bytes [emitted] js/list.1895c1a.js.gz 15.7 KiB [emitted] js/manifest.b7ee988.js 3.12 KiB 2 [emitted] [immutable] manifest js/vendors.38fec86.js 343 KiB 3 [emitted] [immutable] [big] vendors js/vendors.38fec86.js.LICENSE 423 bytes [emitted] js/vendors.38fec86.js.gz 97.7 KiB [emitted] js/work.ea3817c.js 196 bytes 4 [emitted] [immutable] work /* 移除一个list引用的模块,再次打包,完全符合预期 */ Asset Size Chunks Chunk Names css/app.208b221.css 39 bytes 0 [emitted] [immutable] app css/list.8298c67.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.353f491.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.353f491.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.ca738c5.js 3.67 KiB 0 [emitted] [immutable] app js/list.271a546.js 51.5 KiB 1 [emitted] [immutable] list js/list.271a546.js.LICENSE 120 bytes [emitted] js/list.271a546.js.gz 15.7 KiB [emitted] js/manifest.a2e6ed1.js 3.12 KiB 2 [emitted] [immutable] manifest js/vendors.38fec86.js 343 KiB 3 [emitted] [immutable] [big] vendors js/vendors.38fec86.js.LICENSE 423 bytes [emitted] js/vendors.38fec86.js.gz 97.7 KiB [emitted] js/work.ea3817c.js 196 bytes 4 [emitted] [immutable] work
webpack5
webpack5 对moduleIds & chunkIds的优化,不在是以数字作为id
optimization:{ moduleIds:'deterministic', chunkIds:'deterministic' },
如何使用 hash 做缓存呢?
很多人知道 hash,但是要不项目配置为 hash,不利于做长期缓存,要不前端配置好了,但是不知道如何配合后端做长期缓存,这就涉及到 http 缓存的
Etag - Last-Modified
- 1、客户端请求一个页面 A
- 2、服务器返回页面 A,并在给 A 加上一个 Last-Modified(Mon, 22 Mar 2018 10:10:10 GMT)和 ETag(2e681a-6-5d044840)
- 3、客户端展现该页面,并将页面连同 Last-Modified/ETag 一起缓存
- 4、客户再次请求页面 A,并将上次请求时服务器返回的 Last-Modified/ETag 一起传递给服务器,也就是说发送 If-None-Match 头,这个头的内容 就是 2e681a-6-5d044840,发送 If-Modified-Since(Mon, 22 Mar 2018 10:10:10 GMT)
- 5、服务器判断发送过来的 Etag 和 Last-Modified 与本地匹配,如果没有修改,不返回 200,返回 304,直接返回响应 304 和一个空的响应体,当然响应头也会包含 Last-Modified(Mon, 22 Mar 2018 10:10:10 GMT)和 ETag(2e681a-6-5d044840)
Cache-control
- Cache-control 判断浏览器是否需要发送请求而不需要服务器对比,常见的取值有 private、no-cache、max-age、must- revalidate、no-store 等,默认为 private,Cache-control 值为“no-cache”时,访问此页面不会在 Internet 临时文章夹留下页面备份
- 打开新窗口
- 值为 private、no-cache、must-revalidate,那么打开新窗口访问时都会重新访问服务器。 而如果指定了 max-age 值,那么在此值内的时间里就不会重新访问服务器,例如: Cache-control: max-age=5(表示当访问此网页后的 5 秒 内再次访问不会去服务器)
- 在地址栏回车
- 值为 private 或 must-revalidate 则只有第一次访问时会访问服务器,以后就不再访问。 值为 no-cache,那么每次都会访问。 值为 max-age,则在过期之前不会重复访问
- 按后退按扭
- 值为 private、must-revalidate、max-age,则不会重访问, 值为 no-cache,则每次都重复访问
- 按刷新按扭或者 F6
- 无论为何值,都会重复访问
Expires
- Expires 和 max-age 都可以用来指定文档的过期时间,但是也有不同
- Expires 指定一个绝对的过期时间(GMT 格式)
- max-age 指定的是从文档被访问后的存活时间,这个时间是个相对值(比如:3600s),相对的是文档第一次被请求时服务器记录的 Request_time(请求时间)
- 有的服务器, max-age 是这样计算出来的,expires - request_time
静态资源服务器的缓存
- 如果是第一次访问,请求报文首部不会包含相关字段,服务端在发送文件前做如下处理
- 如服务器支持 ETag,设置 ETag 头
- 如服务器支持 Last-Modified,设置 Last-Modified 头
- 设置 Expires 头 + 设置 Cache-Control 头(设置其 max-age 值)浏览器收到响应后会存下这些标记,并在下次请求时带上与 ETag 对应的请求首部 If-None-Match 或与 Last-Modified 对应的请求首部 If-Modified-Since
- 如果是重复的请求
- 浏览器判断缓存是否过期(通过 Cache-Control 和 Expires 确定, 两者都存在 Cache-Control为主)
- 如果未过期,直接使用缓存内容,也就是强缓存命中,并不会产生新的请求
- 如果已过期,会发起新的请求,并且请求会带上 If-None-Match 或 If-Modified-Since,或者兼具两者(两者都存在Etag 为主)
- 服务器收到请求,进行缓存的新鲜度再验证:
- 首先检查请求是否有 If-None-Match 首部,没有则继续下一步,有则将其值与文档的最新 ETag 匹配,失败则认为缓存不新鲜,成功则继续下一步
- 接着检查请求是否有 If-Modified-Since 首部,没有则保留上一步验证结果,有则将其值与文档最新修改时间比较验证,失败则认为缓存不新鲜,成功则认为缓存新鲜
- 当两个首部皆不存在或者验证结果是不新鲜时,发送 200 及最新文件,并在首部更新新鲜度。
- 当验证结果是缓存仍然新鲜时(也就是弱缓存命中),不需发送文件,仅发送 304,并在首部更新新鲜度
- 浏览器判断缓存是否过期(通过 Cache-Control 和 Expires 确定, 两者都存在 Cache-Control为主)
max-age 配合 hash 做使用
在保持 hash 不变性的前提下,我们可以使用 max-age 来设置前端缓存
/* 具体设置多少,个人觉得要看升级的频率,在保证hash不变性的前提下,设置1y 比较合理https://expressjs.com/zh-cn/guide/using-middleware.html */ app.use( express.static(path.join(__dirname, "public"), { maxAge: "1y", expires: "1y", Etag: false, lastModified: false, }) );
参考文章
到此这篇关于一文搞懂webpack hash持久化的原理的文章就介绍到这了,更多相关webpack hash持久化内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!
赞 (0)