webpack hash持久化
chrisPaul101755 人气:0理解 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
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,并在首部更新新鲜度
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, }) );
参考文章
加载全部内容