代码已经关联到github: 链接地址 文章有更新也会优先在这,觉得不错可以顺手点个star,这里会持续分享自己的开发经验(:
浏览器缓存
浏览器再次发送请求时:
- 首先判断强缓存,强缓存生效直接使用强缓存(200 from memory、from disk 先从内存,再从硬盘)
- 如果强缓存不生效会判断
- 没有设置协商缓存就直接重新请求(200)
- 设置了协商缓存则由服务器判断缓存是否失效,没有失效就继续使用缓存(304,同强缓存的获取方式),失效则返回新的文件(200)
缓存分类
强缓存
强缓存主要由响应消息头 Cache-Control
和 Expires
两个 Header
决定的,在一定时间内不向后端请求,直接使用内存或者磁盘内的缓存内容,前者的优先级更高。
Cache-Control: max-age=31536000 //缓存时间 | no-store:禁止本地缓存 | no-cache:本地缓存,但是强制每次请求直接发送给源服务器,由服务器判断是否使用缓存
Cache-Control: private;max-age=31536000 //默认是public 允许代理
Expires: Wed, 21 Oct 2020 07:28:00 GMT //该时间之后请求过期 Cache-control 优先级比 Expires 优先级高
注意请求头的消息头也可以设置
Cache-Control
, 表示客户端想要的缓存策略是什么。
一般不经常变动的JS和CSS均会有一个很大的过期时间,搭配上版本号进行缓存策略,减少向后端的请求,如果均不设置只带版本号也可以,因为浏览器自动有缓存的策略。
有些时候,我们会看到Cache-Control: max-age=31536000
,那么浏览器帧的会缓存这个数据到一年吗,答案当然是否定的,设置这个值只是因为它是协议允许的最大值,浏览器不会缓存那么久,当缓存满了会优先删除旧的内容,但是CDN会,这样可以减少服务器的压力。
协商缓存
由 If-none-match/ETags
和 If-Modified-Since/Last-Modified
参数决定,通常在强缓存失效后,向服务端通信,服务端确定是否使用缓存,前者优先级更高。
ETags
是服务器资源的一个唯一标识,请求响应时消息头会带上这个唯一标识,请求头带上上次请求返回的etag(If-none-match
),由服务器判断资源是否改变。- 请求响应时时消息头会带上上次修改时间(
Last-Modified
),请求时消息头会带上这个修改时间(If-Modified-Since
),由服务器判断资源是否改变。
对于频繁变动的资源,服务器端需设置Cache-Control: no-cache
使浏览器每次都请求服务器,且增加返回请求头 ETag 或者 Last-Modified 来验证资源是否有效。这样的做法虽然不能节省请求数量,但是能显著减少响应数据大小。
版本号
- revving 技术 : 不频繁更新的文件会使用特定的命名方式:在
URL
后面(通常是文件名后面)会加上版本号。
Service Work
一个服务器与浏览器之间的中间人角色,如果网站中注册了service worker那么它可以拦截当前网站所有的请求,进行判断(需要编写相应的判断程序),如果需要向服务器发起请求的就转给服务器,如果可以直接使用缓存的就直接返回缓存不再转给服务器。
简单使用
Service Worker是浏览器在后台独立于网页运行的、用JavaScript编写的脚本。
让我们来看看最小的Service Worker长什么样,以及怎么跑起来:
// 不起眼的一行if,除了防止报错之外,也无意间解释了PWA的P:
// 如果浏览器不支持Service Worker,那就当什么都没有发生过
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
// 所以Service Worker只是一个挂在navigator对象上的HTML5 API而已
// scope 参数是可选的,可以用来指定你想让 service worker 控制的内容的子目录。 在这个例子里,我们指定了 '/',表示 根网域下的所有内容。这也是默认值。
navigator.serviceWorker.register('/service-worker.js', { scope: './'}).then(function (registration) {
console.log('我注册成功了');
}, function (err) {
console.log('我注册失败了');
});
});
}
复制代码以上代码,在load事件触发后,下载并注册了service-worker.js这个文件,Service Worker的逻辑,就写在这里:
// service-worker.js
// 虽然可以在里边为所欲为地写任何js代码,或者也可以什么都不写,
// 都不妨碍这是一个Service Worker,但还是举一个微小的例子:
//监听安装事件,install 事件一般是被用来设置你的浏览器的离线缓存逻辑
this.addEventListener('install', function(event) {
//通过这个方法可以防止缓存未完成,就关闭serviceWorker
event.waitUntil(
//caches api: https://developer.mozilla.org/zh-CN/docs/Web/API/CacheStorage
caches.open('v1').then(function(cache) {
//指定要缓存的内容,地址为相对于跟域名的访问路径
return cache.addAll([
'/sw-test/',
'/sw-test/index.html',
'/sw-test/style.css',
'/sw-test/app.js',
'/sw-test/image-list.js',
'/sw-test/star-wars-logo.jpg',
'/sw-test/gallery/bountyHunters.jpg',
'/sw-test/gallery/myLittleVader.jpg',
'/sw-test/gallery/snowTroopers.jpg'
]);
})
);
});
//监听fetch,拦截请求
this.addEventListener('fetch', function(event) {
var response;
event.respondWith(
caches.match(event.request).then(function(response) {
if (response) {
console.log('Found response in cache:', response);
return response;
}
console.log('No response found in cache. About to fetch from network...');
return fetch(event.request).then(function(response) {
console.log('Response from network is:', response);
caches.open('v1').then(function(cache) {
cache.put(event.request, response);
});
return response.clone();
}).catch(function(error) {
console.error('Fetching failed:', error);
throw error;
});
})
);
});
应用
- 缓存静态资源,Service Worker的一大应用是可以利用CacheStorage API来缓存js、css、字体、图片等静态文件。我们可以在Service Worker的install阶段,指定需要缓存的具体文件,在fetch事件的回调函数中,检查请求的url,如果匹配了已缓存的资源,则不再从服务端获取,以此达到提升网页性能的目的。
- 离线体验,将整个页面缓存下来,比如404页面。
详细见:Service Worker 从入门到出门
缓存的位置
缓存主要有4种:Service Worker
、Memory Cache
、 Disk Cache
和 Push Cache
- Service Worker
- Memory Cache,内存中的缓存,主要包含的是当前中页面中已经抓取到的资源(一般是小资源),例如页面上已经下载的样式、脚本、图片等,读取速度快,但是持续时间短,一般页面关闭就被释放。
- Disk Cache,存储在硬盘中的缓存,能缓存各种资源,读取速度比内存慢,但是能缓存的数据多。
- Push Cache,推送缓存是 HTTP/2 中的内容,当以上三种缓存都没有命中时,它才会被使用。它只在会话(Session)中存在,一旦会话结束就被释放。
刷新对于缓存的影响
- 当ctrl+f5强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存。
- 当f5刷新网页时,跳过强缓存,但是会检查协商缓存。
- 浏览器地址栏中写入URL,回车 浏览器发现缓存中有这个文件了,不用继续请求了,直接去缓存拿。
参考
Service Worker 从入门到出门
浏览器帧的会缓存一年吗
DNS 缓存
有DNS的地方,就有缓存。浏览器、操作系统、Local DNS、根域名服务器,它们都会对DNS结果做一定程度的缓存:
- 首先查找浏览器自身的缓存
- 然后是本机hosts,如果还是没有,则查找本地DNS服务器
- 本地DNS服务器一般是动态分配的或者我们自己指定的地址(当然这里还有个路由器的缓存)
- 再查找不到,则向根服务器发送递归查找,边缘DNS > 顶级DNS > 二级DNS > 三级DNS(也就是网站注册的)
CDN缓存
在浏览器本地缓存失效后,浏览器会向CDN边缘节点发起请求。类似浏览器缓存,CDN边缘节点也存在着一套缓存机制。CDN边缘节点缓存策略因服务商不同而不同,但一般都会遵循http标准协议,通过http响应头中的Cache-control
判断资源是否过期
其优势是:
- CDN节点解决了跨运营商和跨地域访问的问题,访问延时大大降低。
- 大部分请求在CDN边缘节点完成,CDN起到了分流作用,减轻了源服务器的负载。