web性能优化(存储方面)
个人理解, 存储方面的优化其实也是为了减少 http 请求, 节省网络传输带来耗时影响...
把我们需要的文件资源缓存在本地, 或者缓存在代理服务器, 源服务器等. 都是为了优化资源访问的过程...这里我们单纯的从前端能操作的存储来讲, 服务端的手段会介绍下,毕竟全栈也是我们的目标,ememm...
1.浏览器缓存(浏览器)
浏览器缓存机制有几个方面,它们按照获取资源时请求的优先级依次排列如下Memory Cache/Disk Cache
, Service Worker Cache
, HTTP Cache
, Push Cache(http2)
.
HTTP Cache(重点)
我们可以根据不同的资源缓存需求去设置的缓存策略, 当然事先将资源进行分类
, 然后才去设定对应的缓存策略
;
我们可以把http缓存策略按阶段分为缓存存储策略
, 缓存过期策略
, 缓存协商对比策略
三个阶段来理解.
先看下关于缓存头的几个字段, 优先级 http1.1字段高
version | header key | desc | 存储策略 | 过期策略 | 协商对比策略 |
---|---|---|---|---|---|
HTTP1.0 | Pragma | 指定缓存机制: no-cache | √ | ||
HTTP1.1 | Cache-Control | 指定缓存机制 | √ | √ | |
HTTP1.0 | Expires | 指定缓存的过期时间 | √ | ||
HTTP1.0 | res: Last-Modified | 资源最后一次的修改时间 | √ | ||
HTTP1.0 | req: If-Modified-Since | 缓存校验字段,值为资源最后一次的修改时间, 即上次收到的Last-Modified值 | √ | ||
HTTP1.1 | res: ETag | 唯一标识请求资源的字符串 | √ | ||
HTTP1.1 | req: If-None-Match | 缓存校验字段, 值为唯一标识请求资源的字符串, 即上次收到的ETag值 | √ |
缓存存储策略
用来确定 Http 响应内容是否可以被客户端缓存,以及可以被哪些客户端缓存;
这个策略的作用只有一个, 用于决定 Http 响应内容是否可缓存到客户端.
- cache-control的值设置
public: 资源将被客户端和代理服务器缓存
private: 资源仅被客户端缓存, 代理服务器不缓存
no-store 请求和响应都不缓存
no-cache 相当于max-age:0,must-revalidate即资源被缓存,但是缓存立刻过期, 同时下次访问时强制验证资源有效性
max-age 缓存资源, 但是在指定时间(单位为秒)后缓存过期
s-maxage 同上, 依赖public设置, 覆盖max-age, 且只在代理服务器上有效.
max-stale 指定时间内, 即使缓存过时, 资源依然有效
min-fresh 缓存的资源至少要保持指定时间的新鲜期
must-revalidation / proxy-revalidation 如果缓存失效, 强制重新向服务器(或代理)发起验证(因为max-stale等字段可能改变缓存的失效时间)
only-if-cached 仅仅返回已经缓存的资源, 不访问网络, 若无缓存则返回504
no-transform 强制要求代理服务器不要对资源进行转换, 禁止代理服务器对 Content-Encoding, Content-Range, Content-Type字段的修改(因此代理的gzip压缩将不被允许)
- Pragma
用来兼容 http1.0, 其值为 no-cache
缓存过期策略
客户端用来确认存储在本地的缓存数据是否已过期,进而决定是否要发请求到服务端获取数据;
这个策略的作用也只有一个,那就是决定客户端是否可直接从本地缓存数据中加载数据并展示(否则就发请求到服务端获取)
通过设置:
Expires: Wed, 29 Apr 2020 07:45:47 GMT 缓存到期时间
Cache-Control: max-age=6000 缓存资源, 但是在指定时间6000s(单位为秒)后缓存过期
// Cache-Control优先
假设请求资源于5月1日缓存, 且在5月7日过期(时间是相对于请求的时间)
这期间都会命中强缓存
- 强缓存
对于常规请求,只要存在该资源的缓存,且Cache-Control:max-age或者expires没有过期,
那么就能命中强缓存;浏览器便不会向服务器发送请求, 而是直接读取缓存.
Chrome下的现象是 200 OK (from disk cache) 或者 200 OK (from memory cache)
缓存协商对比策略
将缓存在客户端的数据标识发往服务端,服务端通过标识来判断客户端 缓存数据是否仍有效,进而决定是否要重发数据;
客户端检测到数据过期或浏览器刷新后,往往会重新发起一个 http 请求到服务器,服务器此时并不急于返回数据,而是看请求头有没有带标识( If-Modified-Since、If-None-Match)过来,如果判断标识仍然有效,则返回304告诉客户端取本地缓存数据来用即可(这里要注意的是你必须要在首次响应时输出相应的头信息(Last-Modified、ETag)到客户端.
协商缓存
缓存过期后, 继续请求该资源, 对于现代浏览器, 拥有如下两种做法:
- Etag/If-None-Match(优先级高)
根据上次响应中的ETag_value, 自动往request header中添加If-None-Match字段.
服务器收到请求后, 拿If-None-Match字段的值与资源的ETag值进行比较,
若相同, 则命中协商缓存, 返回304响应.
- Last-Modified/If-Modified-Since
根据上次响应中的Last-Modified_value, 自动往request header中添加If-Modified-Since字段.
服务器收到请求后, 拿If-Modified-Since字段的值与资源的Last-Modified值进行比较,
若相同, 则命中协商缓存, 返回304响应
# 缓存协商时间计算
// 利用response: Date 和 Last-Modified返回的时间计算出缓存过期日期
缓存过期日期 = (客户端日期new Date) + (Date_value - Last-Modified_value) / 10
# 所以, 当我们更改了本地时间后可能会出现一些问题; http1.1引入Etag解决了
// 然后缓存过期后, 会再次请求服务端,并携带上 Last-Modified 指定的时间去服务器对比
并根据服务端的响应状态决定是否要从本地加载缓存数据.
说了这么多, 怎么设置这些头部呢?
只能说在客户端(浏览器下)能做的有限, 基本都是在服务端(Nginx)设置的...emm; 所以前端必须学server方面的知识啊!!!是不是有点崩溃呢? 快起来我还能学...
单纯通过前端 html 设置, 也需要浏览器支持,基本没啥用
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"/>
Memory Cache/Disk Cache
MemoryCache,是指存在内存中的缓存.从优先级上来说,它是浏览器最先尝试去命中的一种缓存.从效率上来说,它是响应速度最快的一种缓存; 浏览器有一套自己的规则, 会将较小的文件 base64图片,css,js 等放进去, 内存的分配是有限的
, 如果文件过大或者分配内存不足, 文件会放到 Disk 磁盘中...
Service Worker Cache(PWA的基础之一)
Service Worker MDN
Service Worker 是一种独立于主线程之外的 Javascript 线程.它脱离于浏览器窗体,因此无法直接访问 DOM.这样独立的个性使得 Service Worker 的“个人行为”无法干扰页面的性能,这个“幕后工作者”可以帮我们实现离线缓存、消息推送和网络代理等功能.我们借助 Service worker 实现的离线缓存就称为 Service Worker Cache.
Service Worker 的生命周期包括 install、active、working 三个阶段.一旦 Service Worker 被 install,它将始终存在,只会在 active 与 working 之间切换,除非我们主动终止它.这是它可以用来实现离线存储的重要先决条件.
- Service Worker实现离线缓存
// mian.js
if ('serviceWorker' in navigator) {
window.navigator.serviceWorker.register('/demo.js').then(
function () {
console.log('注册成功')
}).catch(err => {
console.error("注册失败")
})
} else {
console.error("The current browser doesn't support service workers")
}
// demo.js: 缓存的文件分别是 test.html,test.css 和 test.js
// Service Worker会监听 install事件,我们在其对应的回调里可以实现初始化的逻辑
self.addEventListener('install', event => {
event.waitUntil(
// 考虑到缓存也需要更新,open内传入的参数为缓存的版本号
caches.open('test-v1').then(cache => {
return cache.addAll([
// 此处传入指定的需缓存的文件名
'/test.html',
'/test.css',
'/test.js'
])
})
)
})
// Service Worker会监听所有的网络请求,网络请求的产生触发的是fetch事件,
// 我们可以在其对应的监听函数中实现对请求的拦截,进而判断是否有对应到该请求的缓存,
// 实现从Service Worker中取到缓存的目的
// Server Worker 对协议是有要求的,必须以 https 协议为前提
self.addEventListener('fetch', event => {
event.respondWith(
// 尝试匹配该请求对应的缓存值
caches.match(event.request).then(res => {
// 如果匹配到了,调用Server Worker缓存
if (res) {
return res;
}
// 如果没匹配到,向服务端发起这个资源请求
return fetch(event.request).then(response => {
if (!response || response.status !== 200) {
return response;
}
// 请求成功的话,将请求缓存起来.
caches.open('test-v1').then(function(cache) {
cache.put(event.request, response);
});
return response.clone();
});
})
);
});
Push Cache
了解不多, 参考此篇文章https://jakearchibald.com/201...
- 一些特性
- Push Cache 是缓存的最后一道防线.
浏览器只有在 Memory Cache、HTTP Cache 和 Service Worker Cache 均未命中的情况下才会去询问 Push Cache.
- Push Cache 是一种存在于会话阶段的缓存,当 session 终止时,缓存也随之释放.
- 不同的页面只要共享了同一个 HTTP2 连接,那么它们就可以共享同一个 Push Cache
2.浏览器本地存储
这个就是我们前端可以操作的部分了...
cookie, localStorage. sessionStorage
IndexedDB
IndexedDB MDN
这里也不做细讲了, 参考张鑫旭大佬的文章https://www.zhangxinxu.com/wo...
参考
http header
Service Worker MDN
http2 push
IndexedDB MDN
web worker
IndexedDB Demo
Service Worker 参考文章
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。