缓存一直以来都是用来提高性能的一项必不可少的技术 , 利用这项技术可以很好地提高web的性能。 缓存可以很有效地降低网络的时延,同时也会减少大量请求对于服务器的压力。 接下来这篇文章将会详细地介绍在web领域中缓存的一些知识点和应用。

从HTTP协议开始说起

由于整个网络服务都是基于http协议 的,因此先来介绍一下HTTP协议当中定义的缓存机制。HTTP协议主要是通过请求头当中的一些字段来和服务器进行通信,从而采用不同的缓存策略。
一般来说,对于一个完整的HTTP GET请求缓存过程会包含七个主要的步骤:①从接收网络请求开始,②客户端会读取请求报文并且对报文进行解析, 进而提取URL和各种首部,③然后将会查询是否在本地有副本,如果本地没有副本就会从服务器上获取一份副本并且保存在本地。④接着会进行查看副本是否足够新鲜(新鲜度检测), 如果缓存已经失效就会询问服务器是否有任何更新,⑤服务器就会用新的首部和已缓存的主体来构建一条响应报文,⑥最后发送给客户端。⑦根据服务器的不同,会可选地选择创建日志记录该过程。
具体的流程可以看下面这张图(该图来自HTTP权威指南):

根据缓存处理方式的不同,接着又会分为两类:强缓存和协商缓存。

强缓存

强缓存主要是采用响应头中的Cache-ControlExpires两个字段进行控制的。其中Expires是HTTP 1.0中定义的,它指定了一个绝对的过期时期。而Cache-Control是HTTP 1.1时出现的缓存控制字段。Cache-Control:max-age定义了一个最大使用期,就是从第一次生成文档到缓存不再生效的合法生存日期。由于Expires是HTTP1.0时代的产物,因此设计之初就存在着一些缺陷,如果本地时间和服务器时间相差太大,就会导致缓存错乱。这两个字段同时使用的时候Cache-Control的优先级给更高一点。
这两个字段的效果是类似的,客户端都会通过对比本地时间和服务器生存时间来检测缓存是否可用。如果缓存没有超出它的生存时间内,客户端就会直接采用本地的缓存。如果生存日期已经过了,这个缓存也就宣告失效。接着客户端将再次与服务器进行通信来验证这个缓存是否需要更新。

协商缓存

强缓存机制如果检测到缓存失效,就需要进行服务器再验证。这种缓存机制也称作协商缓存。浏览器在第一次获取请求的时候,就会在响应头中携带上资源的上次服务器修改日期(Last-Modified)或者资源的标签(Etag)。后续的请求服务器会根据请求头上的If-Modified-Since(对应Last-Modified)和(If-None-Match)字段来判断资源是否失效,一旦资源过期,则服务器会重新发送新的资源到客户端上,从而保证资源的有效性。
其中Last-Modified字段对应的是资源最后修改时间,例如:Last-Modified: Sat, 30 Dec 2017 20:18:56 GMT ,当客户端再次请求该资源的时候,会在其请求头上附带上If-Modified-Since字段,值就是之前返回的Last-Modified值。如果资源未过期,命中缓存,服务器就直接返回304状态码,客户端直接使用本地的资源。否则,服务器重新发送响应资源。
另外一种协商缓存的校验方式的通过校验码而不是时间,这样就保证了在文件内容不变的情况下不会重复占用网络资源。响应头中Etag字段是服务器给资源打上的一个标记,利用这个标记就可以实现缓存的更新。后续发起的请求,会在请求头上附带上If-None-Match字段,其值就是这个标记的值。
需要注意的是当响应头中同时存在EtagLast-Modified的时候,会先对Etag进行比对,随后才是Last-Modified

浏览器缓存

上面介绍了网络协议层面的缓存方案,接下来从前端的角度来看一下浏览器中几种常用的缓存技术。

localstorage

本来HTTP协议的缓存方案很美好了,不过当用户主动触发页面刷新内容,如:F5等,就会使浏览器的强缓存失效,进而转变成协商缓存。而利用LocalStorage可以无视用户主动刷新行为,并且可以存储较大体积的资源(2M以上)。
localStorage的使用也较为简单:

const key = 'scq000';
const value = 'hello world';// 存
localStorage.setItem(key, value);// 取
localStorage.getItem(key);

虽说localStorage一般是用来存储应用数据的,但是也可以利用其存储js和css等静态资源。

<script id="testJs" src="example.js"></script>
// 以js为例
var lsKey = 'loadJSv1.0'; // 作为localStorage存取的key;// 获取要缓存或者执行的源码内容
function getScriptContent(url, callback) {var httpRequest = new XMLHttpRequest();httpRequest.onreadystatechange = function() {if (httpRequest.readyState === 4) {if (httpRequest.status === 200) {// 获取代码内容var codeStr = httpRequest.responseText;callback && callback(codeStr);}}};httpRequest.open('GET', url);httpRequest.send();
}// 第一次运行的时候缓存
function cacheJs(url) {// 获取代码内容 getScriptContent(url, function(codeStr) {console.log(codeStr);// 执行代码并缓存var script = document.createElement('script');script.innerHTML = codeStr;localStorage.setItem(lsKey, codeStr);});
}// 加载源码
function loadJs(url) {// 读取缓存var cacheStr = localStorage.getItem(lsKey);if(cacheStr) {// 插入浏览器中,或者也可以直接使用eval执行var script = document.createElement('script');script.innerHTML = cacheStr;console.log("使用缓存成功");} else {// 没有缓存,就会从服务器获取源码并缓存到本地cacheJs(url);}
}// 第一次执行的时候,会直接执行并缓存到localhost中去,第二次进入的时候,会直接使用缓存
loadJs('http://code.jquery.com/jquery-3.2.1.min.js')

上面只是一个简单的demo,如果真的要使用这种方案,还需要考虑到更新处理问题。
作为一种性能优化的方案,这种方法也曾被大量应用于移动端的网页中。不过缺点也很明显,由于localStorage是保存在本地中的,所以很容易导致xss注入攻击。如果要使用这种方案,一定要做好对应的安全措施。

Service Worker

作为AppCache的替代方案,Service Worker 是一个相对来说比较新的技术,其目的也主要是为了提高web app的用户体验,可以实现离线应用消息推送等等一系列的功能, 从而进一步缩小与原生应用的差距。
Service Worker可以看做是一个独立于浏览器的Javascript代理脚本,通过JS的控制,能够使应用先获取本地缓存资源(Offline First),从而在离线状态下也能提供基本的功能。
出于安全性的考虑,Service Worker 只能在https协议下使用,不过为了便于开发,现在的浏览器默认支持localhost使用Service Worker。
Service Worker整个的使用过程包括了注册,安装,激活,睡眠销毁等等一系列的状态。

注册

首先需要在页面中注册一个Service Worker。需要写在入口文件中:

if(‘serviceWorker' in navigator) {navigator.serviceWorker.register('./testSW.js', {scope: '/src'}).then(reg => {console.log('service worker is working', reg);    }).catch(e => console.log('register service worker failed'));
}

由于兼容性的问题,需要在代码开始做浏览器特性检测处理。注册时候,scope参数是可选的,用来限制SW的工作范围的。

安装和激活
// 用来标记缓存
const CACHE_FILE = 'my-sw-demo-v1';
let filesToCache = ['/','/index.html','/scripts/main.js','/styles/main.css'
];// 安装
self.addEventListener('install', event => {event.waitUntil(caches.open(CACHE_FILE).then(cache => cache.addAll(filesToCache)););
});// 添加fetch事件监听
self.addEventListener('fetch', event => {event.responseWith(caches.match(event.request).then(response => response).catch(() => fetch(event.request)););
});

当用户首次访问页面的时候,会触发SW的安装事件,addAll方法接收需要被缓存文件的url列表,并会自动获取这些文件存入缓存中。
接下来注册的fetch事件监听器会在每次SW被控制的资源请求时触发,拦截请求并在缓存中匹配对应资源。如果缓存命中,则直接返回资源,否则去发起fetch请求。
当然,如果你想更进一步,可以在缓存没有命中的时候,获取资源然后将获取到资源加入缓存中。另外,在网络不可用的时候,提供一种回退方案。上面的代码可以改写成这样:

// 添加fetch事件监听
self.addEventListener('fetch', event => {event.respondWith(caches.match(event.request).catch(() => {return fetch(event.request).then(response => {return caches.open('v1').then(cache => {cache.put(event.request, response.clone());return response;});});}).catch(() => {// 回退资源return caches.match('/fallback.html');}));
});
更新资源

如果应用中SW已经安装,但是刷新的时候检测到有新版保持可用,就会自动安装。但是需要注意的是,只有当不再有任何已加载页面在使用旧版SW的时候,新版本的SW才会被激活。
咱们把上面的版本号更改一下:

const CACHE_FILE = 'my-sw-demo-v2';

此时刷新页面,当install事件发生的时候,前一个版本(my-sw-demo-v1)如果还在被其它页面使用,则这个新版本不会被激活,当所有页面都不再使用v1的时候,v2就会激活并开始响应请求。

删除旧缓存

作为缓存的完整生命周期来说,提供删除功能必不可少。我们有时候需要手动删除旧版本的缓存,以便释放有限的浏览器缓存空间。此时,可以利用activate事件和waitUntil这样一个方法来清理缓存。

// 清理缓存操作
self.addEventListener('activate', event => {// 设置白名单,不需要删除的缓存keyconst cacheWhiteList = ['v2'];event.waitUntil(cache.keys().then(keyList => {return Promise.all(keyList.map(key => {if (!cacheWhiteList.includes(key)) {// 如果不在白名单里面,就删除该缓存return cache.delete(key);}}));});)
});

前端性能优化之缓存技术相关推荐

  1. 前端性能优化 - 设置缓存

    前言 前端性能优化系列,记录在优化过程中的问题,可能有十万个为什么,待以后懂了再记录,毕竟太菜啥都不懂. 而且部分优化(设置缓存.gzip压缩.使用CDN加速服务)非开发人员来控制,而是网站服务器管理 ...

  2. 使用缓存实现前端性能优化——浏览器缓存机制、缓存分类

    前端性能优化探讨及浏览器缓存机制 一.缓存如何实现前端性能优化 1.什么是浏览器缓存 2.js请求,一般会有哪些地方有缓存处理? 3.静态资源 ① 什么是静态资源 ② 静态资源的缓存策略 二.缓存的类 ...

  3. 聊天室软件源码前端性能优化,缓存角度的相关分析

    在我们考虑提高聊天室软件源码页面渲染速度之前先来思考一个问题,一个页面的速度由什么决定?显而易见,这里主要包含两方面的影响因素. 1.资源传输时间(tcp链接时间和响应时间) 2.dom渲染时间 这两 ...

  4. nginx 开启gzip 配置js_前端性能优化之缓存与GZIP

    最近疫情,着实无聊.简单总结点东西,打发时间. 这篇文章主要记录如何在tomcat或nginx中配置前端静态资源的缓存策略,力求简洁明了,不参杂其他无关配置项. 压缩 对于HTTP的压缩,是一种使用C ...

  5. 前端性能优化基础知识--幕课网

    作为一个前端小码农,在页面样式都能实现以后,就开始考虑:同一个效果,我该用什么样的方式和代码去实现它比较规范?前两天逛幕课网发现了两门课程–<前端性能优化-基础知识认知>和<前端性能 ...

  6. 大型网站技术架构(3):WEB 前端性能优化

    上次说到了性能优化策略,根据网站的分层架构,可以大致的分为 web 前端性能优化,应用服务器性能优化,存储服务器性能优化三大类 这次来说一下 web 前端性能优化,一般来说,web 前端就是应用服务器 ...

  7. 前端性能优化--预加载技术

    当我们谈到前端的性能时,总是会提到比如合并.压缩.缓存或者在服务器上开启gzip之类的,目的都是为了让页面加载的更快. 资源预拉取(prefetch)则是另一种性能优化的技术.通过预拉取可以告诉浏览器 ...

  8. 深度讲解:web前端性能优化

    一.课程简介: 1.课程大纲 涉及到的分类 网络层面 构建层面 浏览器渲染层面 服务端层面 涉及到的功能点 资源的合并与压缩 图片编解码原理和类型选择 浏览器渲染机制 懒加载预加载 浏览器存储 缓存机 ...

  9. WEB前端性能优化小结

    1. 请减少HTTP请求 基本原理: 在浏览器(客户端)和服务器发生通信时,就已经消耗了大量的时间,尤其是在网络情况比较糟糕的时候,这个问题尤其的突出. 一个正常HTTP请求的流程简述:如在浏览器中输 ...

最新文章

  1. 牛腩新闻发布系统(2)使用存储过程查询表
  2. 检测和测试停滞的流– RxJava常见问题解答
  3. ★Linux磁盘配额的使用 ★——牛刀小试
  4. Win10+Torch1.9+CUDA11.1成功配置YOLOX预测环境
  5. android获取mp3/mp4媒体信息
  6. 菜鸟教程:零基础HTML入门
  7. 什么样的家具拆单软件才能称之为好用?全屋定制拆单 衣柜橱柜拆单 sketchup拆单 拆单软件 有屋拆单软件 筑木
  8. 阿里云消息服务(MNS)简单介绍
  9. centos7系统开启ftp服务器,centos7 开启ftp服务器
  10. 2021-2027全球与中国GPS智能手表市场现状及未来发展趋势
  11. 金色经典图案背景新中式PPT模板
  12. 一张图+一个Box+一个TextArea带你DIY不一样的数字键盘
  13. ubuntu git clone 报错error: RPC failed; curl 56 GnuTLS recv error (-9): A TLS packet with unexpected
  14. python中文件操作
  15. 考研院校(转载自西邮学子)
  16. 2018中国区域农业品牌影响力排行榜发布,100个知名品牌当选
  17. Joomla模板制作教程:菜单
  18. 职场小白必学技巧之:手把手教你分割PDF怎么操作
  19. java 音频倍速播放,libsonic - 倍速播放开源库
  20. 多分类RandomForest回归及ROC曲线绘制

热门文章

  1. 华硕v4000fj笔记本怎么样_所有已开箱笔记本的目录汇总 20200812
  2. 输出正反等腰三角形(菱形)
  3. Windows Server 2008域中组的简析
  4. Lock应用之 读写锁
  5. C#线程锁使用全功略
  6. Spring Boot 启动流程
  7. 016、JVM实战总结:大厂面试题:JVM中有哪些垃圾回收算法,每个算法各自的优劣?
  8. 【调试工具】之IOS真机测试
  9. 【汇编优化】ARM Intrinsic优化
  10. C++自学20:指针/指针的指针/const