前端缓存
http缓存
Expires
HTTP1.0的内容,服务器使用Expires头来告诉Web客户端它可以使用当前副本,直到指定的时间为止。
Cache-Control
HTTP1.1引入了Cathe-Control,它使用max-age指定资源被缓存多久,主要是解决了Expires一个重大的缺陷,就是它设置的是一个固定的时间点,客户端时间和服务端时间可能有误差。
Last-Modified / If-Modified-Since
Last-Modified是服务器告诉浏览器该资源的最后修改时间,If-Modified-Since是请求头带上的,上次服务器给自己的该资源的最后修改时间。然后服务器拿去对比。
若资源的最后修改时间大于If-Modified-Since,说明资源又被改动过,则响应整片资源内容,返回状态码200;
若资源的最后修改时间小于或等于If-Modified-Since,说明资源无新修改,则响应HTTP 304,告知浏览器继续使用当前版本。
Etag / If-None-Match
前面提到由文件的修改时间来判断文件是否改动,还是会带来一定的误差,比如注释等无关紧要的修改等。所以推出了新的方式。
Etag是由服务端特定算法生成的该文件的唯一标识,而请求头把返回的Etag值通过If-None-Match再带给服务端,服务端通过比对从而决定是否响应新内容。这也是304缓存。
浏览器缓存
Storage
简单的缓存方式有cookie,localStorage和sessionStorage。
前端数据库
前端数据库有WebSql和IndexDB,其中WebSql被规范废弃,他们都有大约50MB的最大容量,可以理解为localStorage的加强版。
应用缓存 -- PWA
应用缓存主要是通过manifest文件来注册被缓存的静态资源,已经被废弃,因为他的设计有些不合理的地方,他在缓存静态文件的同时,也会默认缓存html文件。这导致页面的更新只能通过manifest文件中的版本号来决定。所以,应用缓存只适合那种常年不变化的静态网站。如此的不方便,也是被废弃的重要原因。
PWA也运用了该文件,不同于manifest简单的将文件通过是否缓存进行分类,PWA用manifest构建了自己的APP骨架,并运用Servie Worker来控制缓存
Service Worker
简述
浏览器支持情况:
通俗来说,PWA就是渐进式web应用(Progressive Web App),service worker做为PWA的核心技术之一,多年来一直被Google大力推广。
- 基于web worker(一个独立于JavaScript主线程的独立线程,在里面执行需要消耗大量资源的操作不会堵塞主线程)
- 在web worker的基础上增加了离线缓存的能力
- 本质上充当Web应用程序(服务器)与浏览器之间的代理服务器(可以拦截全站的请求,并作出相应的动作->由开发者指定的动作)
- 创建有效的离线体验(将一些不常更新的内容缓存在浏览器,提高访问体验)
- 由事件驱动的,具有生命周期
- 可以访问cache和indexDB
- 支持推送
- 并且可以让开发者自己控制管理缓存的内容以及版本
生命周期:
1、register -- 注册
navigator.serviceWorker判断浏览器是否支持serviceWorker,
register注册sw,
scope 指定sw作用路由,
registration.update 更新sw,
registration.unregister 卸载sw
2、install
install 事件是 SW 触发的第一个事件,并且仅触发一次。
installEvent.waitUntil()接收一个 Promise 参数,用它来表示 SW 安装的成功与否。
SW 在安装成功并激活之前,不会响应 fetch或push等事件。
默认情况下,页面的请求(fetch)不会通过 SW,除非它本身是通过 SW 获取的,也就是说,在安装 SW 之后,需要刷新页面才能有效果。
clients.claim()可以改变这种默认行为
3、active
如果是第一次加载sw,在安装后,会直接进入activated阶段,而如果sw进行更新,情况就会显得复杂一些。流程如下:
首先老的sw为A,新的sw版本为B。
B进入install阶段,而A还处于工作状态,所以B进入waiting阶段。只有等到A被terminated后,B才能正常替换A的工作。
这个terminated的时机有如下几种方式:
1 关闭浏览器一段时间;2 手动清除Service Worker;3 在sw安装时直接跳过waiting阶段.
4、idle
这个空闲状态一般是不可见的,这种一般说明sw的事情都处理完毕了,然后处于闲置状态了。浏览器会周期性的轮询,去释放处于idle的sw占用的资源。
5、fetch
该阶段是sw最为关键的一个阶段,用于拦截代理所有指定的请求,并进行对应的操作。
Workbox
简述
在 Workbox 之前,GoogleChrome 团队较早时间推出过 sw-precache 和 sw-toolbox 库,但是在 GoogleChrome 工程师们看来,Workbox 才是真正能方便统一的处理离线能力的更完美的方案,所以停止了对 sw-precache 和 sw-toolbox 的维护。
配置
首先,需要在项目的sw.js文件中,引入Workbox的官方js,其中importScripts是webworker中加载js的方式。
引入Workbox后,全局会挂载一个Workbox对象,也可以统一指定存储时Cache的名称,如下:
importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.0.0-alpha.3/workbox-sw.js');
if (workbox) {
console.log('workbox加载成功');
} else {
console.log('workbox加载失败');
}
//设置缓存cachestorage的名称
workbox.core.setCacheNameDetails({
prefix:'workbox-test',
suffix:'v2
'});
workbox.precaching.precacheAndRoute([
{ url: './styles/workbox.css', revision: '2' }, { url: './workbox', revision: '2' }
]);
precache (预缓存) 静态文件
Workbox的缓存分为两种,一种的precache,一种的runtimecache。
工作原理
首次加载Web应用程序时,Workbox会下载指定的资源,并存储具体内容和相关修订的信息在indexedDB中。
当资源内容和sw.js更新后,Workbox会去比对资源,然后将新的资源存入Cache,并修改indexedDB中的版本信息。
这个时候我们把main.css的内容改变后,再刷新页面,会发现除非强制刷新,否则Workbox还是会读取Cache中存在的老的main.css内容。
即使我们把main.css从服务器上删除,也不会对页面造成影响。
所以这种方式的缓存都需要配置一个版本号
workbox.precaching.precacheAndRoute([
{
url: './styles/workbox.css',
revision: '4'
},
]);
runtimecache路由请求缓存
路由请求缓存是通过文件路由匹配的模式分别对制定的路由文件做不同策略缓存的方式,这部分工作可以在 app/sw.js 中直接使用 workbox 提供的 workbox.routing.registerRoute API 完成,这个 API 可以理解为干了两件事情,一、通过请求路由配置匹配到指定待缓存文件或请求内容,二、通过第二个参数的处理回调函数决定用何种策略来缓存匹配上的文件。
有 三种 方法可以通过 workbox-route 来匹配一个请求 URL
1字符串方式,
workbox.routing.registerRoute( './imgs/logo.jpeg', handler);
workbox.routing.registerRoute( 'http://localhost:3000/imgs/logo.jpeg', handler);
2正则表达式方式,
workbox.routing.registerRoute(new RegExp('.*\.logo.jpeg'), handler);
3回调函数方式
const matchFunction = ({url, event}) => /imgs/.test(url));
workbox.routing.registerRoute(matchFunction, handler);
三种方式匹配的资源怎么处理呢?配置handler,通常两种做法
1使用一种 workbox 通过workbox.strategiesAPI 提供的缓存策略。
2提供一个自定义返回带有返回结果的 Promise 的回调方法。
Stale While Revalidate
这种策略的意思是当请求的路由有对应的 Cache 缓存结果就直接返回,在返回 Cache 缓存结果的同时会在后台发起网络请求拿到请求结果并更新 Cache 缓存,如果本来就没有 Cache 缓存的话,直接就发起网络请求并返回结果,这对用户来说是一种非常安全的策略,能保证用户最快速的拿到请求的结果,但是也有一定的缺点,就是还是会有网络请求占用了用户的网络带宽。可以像如下的方式使用State While Revalidate策略:
workbox.routing.registerRoute(
'/imgs/logo.jpeg',
workbox.strategies.staleWhileRevalidate()
);
Network First
这种策略就是当请求路由是被匹配的,就采用网络优先的策略,也就是优先尝试拿到网络请求的返回结果,如果拿到网络请求的结果,就将结果返回给客户端并且写入 Cache 缓存,如果网络请求失败,那最后被缓存的 Cache 缓存结果就会被返回到客户端,这种策略一般适用于返回结果不太固定或对实时性有要求的请求,为网络请求失败进行兜底。可以像如下方式使用Network First策略:
workbox.routing.registerRoute(
'/imgs/logo.jpeg',
workbox.strategies.networkFirst()
);
Cache First
这个策略的意思就是当匹配到请求之后直接从 Cache 缓存中取得结果,如果 Cache 缓存中没有结果,那就会发起网络请求,拿到网络请求结果并将结果更新至 Cache 缓存,并将结果返回给客户端。这种策略比较适合结果不怎么变动且对实时性要求不高的请求。可以像如下方式使用 Cache First 策略:
workbox.routing.registerRoute(
'/imgs/logo.jpeg',
workbox.strategies.cacheFirst()
);
Network Only
比较直接的策略,直接强制使用正常的网络请求,并将结果返回给客户端,这种策略比较适合对实时性要求非常高的请求。可以像如下方式使用Network Only策略
workbox.routing.registerRoute(
'/imgs/logo.jpeg',
workbox.strategies.networkOnly()
);
Cache Only
这个策略也比较直接,直接使用 Cache 缓存的结果,并将结果返回给客户端,这种策略比较适合一上线就不会变的静态资源请求。可以像如下方式使用Cache Only策略:
workbox.routing.registerRoute(
'/imgs/logo.jpeg',
workbox.strategies.cacheOnly()
);
自定义策略
如果以上的那些策略都不太能满足你的请求的缓存需求,那就得想想办法自己定制一个合适的策略,甚至是不同情况下返回不同的请求结果,workbox 也考虑到了这种场景