目录
- 1 强缓存和协商缓存
- 1.1 强缓存
- expires
- cache control
- 1.2协商缓存
- last-modified/if-modified-since
- etag/if-none-match
- 1.1 强缓存
- 2 缓存位置
- 2.1 service worker
- 2.2 memory chache
- 2.3 disk cache
- 2.4 push cache
- 3 扩展
- 3.1 etag 的优先级为什么比 last-modified 高
- 3.2 缓存注意点
- 3.3 webpack hash
- 3.4 service worker
1 强缓存和协商缓存
- 强缓存和协商缓存是通过 http 的缓存策略
- 强缓存有2中 cache-control、expires
- 强缓存不会发送真实请求给服务端,状态码是 200
- 协商缓存也有2种(last-modified/if-modified-since)、(etag/if-none-match)
- 协商缓存是通过客户端和服务共同协商的,状态码是 304
1.1强缓存
expires
绝对时间,将服务端时间和浏览器时间做对比,如果服务端时间和客服端时间有差异,就会出现问题
cache-control
相对时间,可以更精准的控制缓存,有以下一些值
- no-cache 需要先和服务器确认返回的响应的编号是否发生了变化,如果没变化就用缓存
- no-store 禁止使用缓存
- max-age 相对时间秒数
- private 只允许终端客户端进行缓存,中间层不允许
- public 允许客户端、cdn等中间服务层进行缓存
1.2 协商缓存
last-modified/if-modified-since
- last-modified 文件最后修改日期
etag/if-none-match
- etag 根据文件内容生成一个唯一值
- etag 都优先级比 last-modified 高
2 缓存位置
2.1 Service Worker
- service worker 是一个注册在指定源和路径下的事件驱动的 worker,它采用JavaScript 控制关联页面或者网站,拦截并修改访问请求,细粒度地缓存资源,相当于在客户端和服务端做了一个中间件来有效控制请求的缓存
- service worker 运行在 worker 上下文, 因此不能访问 dom
- 设计完全异步, 不能用 xhr 和 localstorage
- service worker 是独立于主线程的独立线程,用来做服务器推送,缓存等功能
- service worker 首先要注册,注册成功后就可以缓存此域名下所有的 fetch 事件
- 必须使用 https,因为 service worker 中涉及请求拦截,我们必须使用 https 来保障安全
2.2 Memory Cache
- 内存缓存,储存到内存上,读取速度快,容量小,持续性短,有时间限制,会随着进程释放而释放
2.3 Disk Cache
- 储存在硬盘上,容量大,读取速度慢,读取需要进行I/O操作
2.4 Push Cache
http2中的内容,当上面当缓存都没命中时,才会被使用,缓存时间短,存在session中,会话结束而释放
缓存的执行顺序: Service Work -> Memory Cache -> Disk Cache -> Push Cache
3扩展
3.1 etag 的优先级为什么比 last-modified 高
- 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候,我们并不希望客户端认为这个文件被修改了,而重新 get
- 某些文件修改非常频繁,比如在秒以下的时间内进行修改(比方说 1s 内修改了 N 次),If-Modified-Since能检查到的粒度时 s 级的,这种修改无法判断(或者说 UNIX 记录 MTIME只能精确到秒)
- 某些服务器不能精确得到的文件的最后修改时间
3.2 缓存注意点
- 分布式系统里多台机器间文件的Last-Modified必须保持一致,以免负载均衡到不同机器导致比对失败;
- 分布式系统尽量关闭掉ETag(每台机器生成的ETag都会不一样);
3.3 webpack hash
对于客户端,静态资源首次被加载后浏览器都会进行缓存,当资源发生变化我们如何去通知浏览器呢?资源文件命名hash化就是来解决这个问题
hash
- 只要项目中的一个文件改变了,项目构建了,项目文件中所有的 hash 都会变
chunkhash
- 众所周知,根据不同的入口文件进行依赖文件解析、构建对应的chunk,每一个代码块 chunk 对应的一个hash,当相应的文件发生改变,只有当前的 chunk 的 hash 会发生改变,chunkhash 的影响范围比 hash 要小一些
contenthash
- 根据文件内容产生都hash值
需要特别注意的一点,file-loader的hash字段,这个loader自己定义的占位符,和webpack的内置hash字段并不一致。这里的hash是使用md4等hash算法,对文件内容进行hash。所以只要文件内容不变,hash还是会保持一致。
第一种每构建一次,即使文件每变,hash值都变了,缓存就失效了,一般不采用。在项目中,我们一般会把项目中都会用 MiniCssExtractPlugin 把 css 抽离出来,如果我们使用 chunkhash,修改了css后,js 的 hash 也变化了。所以css 一般用contenthash, js用chunkhash
3.4 service worker
- 通过配置sw-precache-webpack-plugin生成我们的service-worker的脚本
- SWPrecacheWebpackPlugin是一个webpack插件,用于使用service worker来缓存外部项目依赖项。 它将使用sw-precache生成service worker文件并将其添加到您的构建目录。为了在service worker中生成预缓存的名单, 这个插件必须应用在assets已经被webpack打包之后
const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin')
const plugins = [
new SWPrecacheWebpackPlugin({
dontCacheBustUrlsMatching: /\.\w{8}\./,
filename: 'service-worker.js',
logger(message) {
...
},
minify: true,
navigateFallback: publicUrl + '/index.html',
navigateFallbackWhitelist: [/^(?!\/__).*/],
staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
}),
...
]
- 注册service worker
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
...
}
};
};
})
.catch(error => {
...
});