什么是浏览器缓存
浏览器保存通过HTTP获取的所有资源,是浏览器将网络资源存储在本地的一种行为。
缓存资源在哪
- memory cache 将资源缓存到内存中
- 只能存储派生类资源文件
- 退出进程时数据会被清除
- 一般保存脚本、字体、图片
- disk cache 将资源缓存到磁盘中
- 只能村粗派生类资源文件
- 退出进程时数据不会被清除
- 一般保存非脚本如css文件
资源分为两类,一类是主资源如html页面或者下载项。一类是派生资源,如html页面内嵌的图片或脚本链接。
因为css文件加载一次就可渲染出来我们不会频繁读取所以它适合缓存到磁盘,而js之类的脚本随时可能执行适合缓存到内存。
三级缓存原理
- 先在内存中查找有则加载
- 内存中不存在则在磁盘中查找有则加载
- 磁盘也没有就进行网络请求
- 请求获取的资源缓存到内存和磁盘。
缓存的作用
- 加快网页加载速度
- 减少冗余数据传输
- 减少服务器负担,提高网站性能
缓存分类
强制缓存
浏览器加载资源时,先根据本地缓存资源的header信息中的expires和cache-control字段判断是否命中强制缓存,如果命中则不会发送请求。
- Expires(http1.0规范)
- 值为一个绝对时间的 GMT 格式的时间字符串,比如 Expires:Mon,18 Oct 2066 23:59:59 GMT。这个时间代表着这个资源的失效时间,在此时间之前,即命中缓存。
- Cache-Control(http1.1规范)
- max-age:值是一个相对时间,例如 Cache-Control:max-age=3600,代表着资源的有效期是 3600 秒
- no-cache:需要进行协商缓存,发送请求到服务器确认是否使用缓存。
- no-store:禁止使用缓存,每一次都要重新请求数据。
- public:可以被所有的用户缓存,包括终端用户和 CDN 等中间代理服务器。
- private:只能被终端用户的浏览器缓存,不允许 CDN 等中继缓存服务器对其缓存。
Cache-Control 与 Expires同时启用时 Cache-Control 优先级高
// 设置强制缓存
res.setHeader("Expires", new Date(Date.now() + 30000).toGMTString());
res.setHeader("Cache-Control", "max-age=30");
协商缓存
当强制缓存没有命中时,浏览器会发送一个请求到服务器,服务器根据header中的Last-Modify/if-Modify-Since和ETag/if-None-Match来判断是否命中缓存。如果命中则返回304,告诉浏览器资源未更新,可以使用本地缓存。
- Last-Modify/if-Modify-Since
浏览器第一次请求资源时,服务器返回的header中会加上Last-Modify,表示该资源的最后修改时间。当浏览器再次请求该资源时,request请求头中会包含if-Modify-Since,该值为缓存之前返回的Last-Modify。服务器收到if-Modify-Since后,根据资源的最后修改时间判断是否命中缓存,如果命中缓存返回304不会返回资源内容也不会返回新的Last-Modify。
缺点:
1.短时间内资源发生了改变,Last-Modified 并不会发生变化。
2.周期性变化。如果这个资源在一个周期内修改回原来的样子了,我们认为是可以使用缓存的,但是 Last-Modified 可不这样认为,因此便有了 ETag。
- ETag/if-None-Match
ETag/if-None-Match返回的是一个校验码。ETag可以保证每个资源都是唯一的,资源变化会导致ETag变化。服务器根据浏览器上发送if-None-Match值来判断是否命中缓存。
Last-Modified 与 ETag 一起使用时,服务器会优先验证 ETag,一致的情况下,才会继续比对 Last-Modified,最后才决定是否返回 304。
// 协商缓存
// 获取文件最后修改时间
let ctime = statObj.ctime.toGMTString();
// 获取加密后的唯一标识
let flag = md5.digest("hex");
// 获取协商缓存的请求头
let ifModifiedSince = req.headers["if-modified-since"];
let ifNoneMatch = req.headers["if-none-match"];
if (ifModifiedSince === ctime || ifNoneMatch === flag) {
// 命中缓存
res.statusCode = 304;
res.end();
} else {
// 设置协商缓存
res.setHeader("Last-Modified", ctime);
res.setHeader("Etag", flag);
}
// 也可以使用第三方中间件
const conditional = require('koa-conditional-get');
const etag = require('koa-etag');
app.use(conditional());
app.use(etag());
总结
浏览器再次访问一个已经访问过的资源时
- 看看是否命中强缓存,如果命中,就直接使用缓存了。
- 如果没有命中强缓存,就发请求到服务器检查是否命中协商缓存。
- 如果命中协商缓存,服务器会返回 304 告诉浏览器使用本地缓存。
- 否则,返回最新的资源。
网友评论