- Follow me on Github
- https://github.com/billshooting
1. 传统的 HTTP 浏览器缓存策略
在一个网页的生命周期中, 开发者为了缩短用户打开页面的时间, 通常会设置很多缓存. 其中包括了:
浏览器缓存
代理服务器缓存(CDN 缓存)
服务器缓存
数据库缓存
等各种缓存. 这些缓存大多数和前端没什么关系, 也不由前端开发者控制, 其中和前端较为密切的是浏览器缓存, 但它本质上也是由服务器控制的.
在 Service Worker 还未问世之前, 浏览器缓存主要是由 HTTP 缓存策略和浏览器内置的存储功能 (cookie,Local Storage,Session Storage 等) 来提供. 其中 HTTP 缓存由于是钦定的, 根正苗红, 浏览器支持的也很好, 是最常用的浏览器缓存技术. 而通过浏览器内置存储功能来实现缓存, 相比之下就没那么高端大气上档次了. 因为这种方式没个标准范式, 虽说可以通过 JS 进行控制显得比 HTTP 缓存灵活, 但效果嘛就只能依赖程序员的水平了, 也没有个统一的轮子能用, 所以这种方式也就是小打小闹, 不成气候.
下面介绍一下 HTTP 缓存的一些用法:
Expires 头部
早在 HTTP 协议被设计的时候, 协议的起草者们就想到了缓存的事情, 自然也有相应的功能, 那就是 Expires 这个头部. 每当浏览器请求时, 服务器可以在相应的报文中附加这个 Expires, 它的典型值看起来是这样的:
Expires: Tue, 01 May 2018 11:37:06 GMT
也就是在该资源在世界协调时
2018/05/01 11:37:06
才过期, 我的请求时间是
2018/05/01 07:37:06
, 所以就是这个资源在 4 小时之后过期, 4 小时之内对该资源的请求都直接使用缓存, 除非你用 Ctrl+F5 刷新. 但是呢, 这种控制明显是不够精细的, 这是个 HTTP1.0 协议中规定的头部. 由于我们现在都用 HTTP2.0 都已经来了, HTTP1.1 已经全面普及了, 这玩意自然已经用的不多了.
Cache-Control 头部
Expires 头部只能控制过期时间, 万一请求的资源在过期时间之前就更新了, 那就可能会出现显示或者功能问题. 为此, HTTP 协议再更新到 1.1 版本的时候, 增加了一个新的头部 Cache-Control 并规定: 如果同时存在 Cache-Control 和 Expires 则前者有效. 它有以下常用的值可选: public private max-age s-maxage no-cache no-store 等. 一个典型的值看起来是以下这样:
Cache-Control: s-maxage=300, public, max-age=60
为了更好的说明各个字段的意义, 先说下浏览器请求资源的步骤:
判断请求是否命中缓存, 如命中则执行步骤 2; 如没有则执行步骤 3;
判断缓存是否过期, 如没有则直接返回; 如过期则执行步骤 3, 并带上缓存信息;
浏览器向服务器请求资源;
服务器判断缓存信息, 如资源尚未更新, 则返回
304
, 如没有缓存信息或则资源已更新则返回
200
, 并把资源返回.
浏览器根据响应头部决定要不要存储缓存(只有 no-store 时不存储缓存信息).
s-maxage 表示共享缓存的时间, 单位是 s, 也就是 5 分钟;
public 表示这是个共享缓存, 可以被其他 session 使用;
max-age 意义与 s-maxage 差不多, 只是它用于 private 的情形;
no-cache 这种策略下, 浏览器会跳过步骤 2, 并带上缓存信息向服务器发起请求.
- //Response:
- Last-Modified: Sat, 01 Jan 2000 00:00:00 GMT
- //Request:
- If-Modified-Since: Sat, 01 Jan 2000 00:00:00 GMT
- Etag/If-None-Match
- Last-Modified/If-Modified-Since
- navigator.service.Worker.register('/sw.js')
- );
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <link href="style/style-1.CSS" rel-"stylesheet">
- </head>
- <body>
- <img src="image/image-1.png" />
- <script async src="js/script-1.js"></script>
- <script>
- if ('serivceWorker' in navigator) {
- navigator.serviceWorker.register('/sw.js')
- .then(reg => console.log('Service worker registered successfully!'))
- .catch(err => console.log('Service worker failed to register!'));
- }
- </script>
- </body>
- </html>
- let cacheName = 'indexCache';
- self.addEventListener('install', event => {
- //waitUntil 接受一个 Promise, 直到这个 promise 被 resolve, 安装阶段才算结束
- event.waitUntil(
- caches.open(cacheName)
- .then(cache => cacheAll(['/style/style-1.css',
- '/image/image-1.png',
- '/script/script-1.js',
- ]))
- );
- });
- // 监听 activate 事件, 可以在这个事件里情况上个 sw 缓存的内容
- self.addEventListener('activate', event => ...}
- // 监听 fetch 事件, 可以拦截所有请求并处理
- self.addEventListener('fetch', event => {
- event.respondWith(
- caches.match(event.request)
- .then(res => {
- //1. 如果请求的资源已被缓存, 则直接返回
- if (res) return res;
- //2. 没有, 则发起请求并缓存结果
- let requestClone = event.request.clone();
- return fetch(requestClone).then(netRes => {
- if(!netRes || netRes.status !== 200) return netRes;
- let responseClone = netRes.clone();
- caches.open(cacheName).then(cache => cache.put(requestClone, responseClone));
- return netRes;
- });
- })
- );
- });
来源: https://www.cnblogs.com/bill-shooting/p/9347441.html