☆前端优化:浏览器缓存技术介绍

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了☆前端优化:浏览器缓存技术介绍相关的知识,希望对你有一定的参考价值。

参考技术A

在前端开发中,性能一直都是被大家所重视的一点,然而判断一个网站的性能最直观的就是看网页打开的速度。 其中提高网页反应速度的一个方式就是使用缓存 。缓存技术一直一来在WEB技术体系中扮演非常重要角色,是快速且有效地提升性能的手段。

一个优秀的缓存策略可以缩短网页请求资源的距离,减少延迟,并且由于缓存文件可以重复利用,还可以减少带宽,降低网络负荷。

所以,缓存技术是无数WEB开发从业人员在工作过程中不可避免的一大问题。 在产品开发的时候我们总是想办法避免缓存产生,而在产品发布之时又在想策略管理缓存提升网页的访问速度 。了解浏览器的缓存命中原理,是开发WEB应用的基础,本文着眼于此,学习浏览器缓存的相关知识,总结缓存避免和缓存管理的方法,结合具体的场景说明缓存的相关问题。希望能对有需要的人有所帮助。

在实际WEB开发过程中,缓存技术会涉及到不同层、不同端,比如:用户层、系统层、代理层、前端、后端、服务端等, 每一层的缓存目标都是一致的,就是尽快返回请求数据、减少延迟 ,但每层使用的技术实现是各有不同,面对不同层、不同端的优劣,选用不同的技术来提升系统响应效率。所以,我们首先看下各层的缓存都有哪些技术,都缓存哪些数据,从整体上,对WEB的缓存技术进行了解,如下图所示:

本篇文章重点讲的就是上面红色框部分缓存内容。

当浏览器请求一个网站的时候,会加载各种各样的资源,比如:html文档、图片、CSS和JS等文件。对于一些不经常变的内容,浏览器会将他们保存在本地的文件中,下次访问相同网站的时候,直接加载这些资源,加速访问。

那么如何知晓浏览器是读取了缓存还是直接请求服务器?如下图网站来做个示例:

第一次打开该网站后,如果再次刷新页面。会发现浏览器加载的众多资源中,有一部分size有具体数值,然而还有一部分请求,比如图片、css和js等文件并没有显示文件大小,而是显示了 from dis cache 或者 from memory cache 字样。这就说明了,该资源直接从本地硬盘或者浏览器内存读取,而并没有请求服务器。

浏览器启用缓存至少有两点显而易见的好处: (1)减少页面加载时间;(2)减少服务器负载;

浏览器是否使用缓存、缓存多久,是由服务器控制的 。准确来说,当浏览器请求一个网页(或者其他资源)时, 服务器发回的响应的「响应头」部分的某些字段指明了有关缓存的关键信息 。下面看下,HTTP报文中与缓存相关的首部字段:

根据上面四种类型的首部字段不同使用策略, 浏览器中缓存可分为强缓存和协商缓存

当浏览器对某个资源的请求命中了强缓存时, 返回的HTTP状态为200 ,在chrome的开发者工具的network里面 size会显示为from cache ,比如:京东的首页里就有很多静态资源配置了强缓存,用chrome打开几次,再用f12查看network,可以看到有不少请求就是从缓存中加载的:

Expires是HTTP 1.0提出的一个表示资源过期时间的header,它描述的是一个绝对时间,由服务器返回,用GMT格式的字符串表示 ,如:Expires:Thu, 31 Dec 2037 23:55:55 GMT,包含了Expires头标签的文件,就说明浏览器对于该文件缓存具有非常大的控制权。

例如,一个文件的Expires值是2020年的1月1日,那么就代表,在2020年1月1日之前,浏览器都可以直接使用该文件的本地缓存文件,而不必去服务器再次请求该文件,哪怕服务器文件发生了变化。

所以, Expires是优化中最理想的情况,因为它根本不会产生请求 ,所以后端也就无需考虑查询快慢。它的缓存原理,如下:

Expires是较老的强缓存管理header, 由于它是服务器返回的一个绝对时间 ,在服务器时间与客户端时间相差较大时,缓存管理容易出现问题, 比如:随意修改下客户端时间,就能影响缓存命中的结果 。所以在HTTP 1.1的时候,提出了一个新的header, 就是Cache-Control,这是一个相对时间,在配置缓存的时候,以秒为单位,用数值表示 ,如:Cache-Control:max-age=315360000,它的缓存原理是:

Cache-Control描述的是一个相对时间 ,在进行缓存命中的时候, 都是利用客户端时间进行判断 ,所以相比较Expires,Cache-Control的缓存管理更有效,安全一些。

这两个header可以只启用一个,也可以同时启用, 当response header中,Expires和Cache-Control同时存在时,Cache-Control优先级高于Expires

此外,还可以为 Cache-Control 指定 public 或 private 标记。 如果使用 private,则表示该资源仅仅属于发出请求的最终用户,这将禁止中间服务器(如代理服务器)缓存此类资源 。对于包含用户个人信息的文件(如一个包含用户名的 HTML 文档),可以设置 private,一方面由于这些缓存对其他用户来说没有任何意义,另一方面用户可能不希望相关文件储存在不受信任的服务器上。需要指出的是,private 并不会使得缓存更加安全,它同样会传给中间服务器(如果网站对于传输的安全性要求很高,应该使用传输层安全措施)。 对于 public,则允许所有服务器缓存该资源 。通常情况下,对于所有人都可以访问的资源(例如网站的 logo、图片、脚本等), Cache-Control 默认设为 public 是合理的

当浏览器对某个资源的请求没有命中强缓存, 就会发一个请求到服务器,验证协商缓存是否命中,如果协商缓存命中,请求响应返回的http状态为304并且会显示一个Not Modified的字符串 ,比如你打开京东的首页,按f12打开开发者工具,再按f5刷新页面,查看network,可以看到有不少请求就是命中了协商缓存的:

查看单个请求的Response Header, 也能看到304的状态码和Not Modified的字符串,只要看到这个就可说明这个资源是命中了协商缓存,然后从客户端缓存中加载的 ,而不是服务器最新的资源:

【Last-Modified,If-Modified-Since】的控制缓存的原理,如下

【Last-Modified,If-Modified-Since】都是根据服务器时间返回的header,一般来说, 在没有调整服务器时间和篡改客户端缓存的情况下,这两个header配合起来管理协商缓存是非常可靠的,但是有时候也会服务器上资源其实有变化,但是最后修改时间却没有变化的情况 ,而这种问题又很不容易被定位出来,而当这种情况出现的时候,就会影响协商缓存的可靠性。 所以就有了另外一对header来管理协商缓存,这对header就是【ETag、If-None-Match】 。它们的缓存管理的方式是:

Etag和Last-Modified非常相似,都是用来判断一个参数,从而决定是否启用缓存。 但是ETag相对于Last-Modified也有其优势,可以更加准确的判断文件内容是否被修改 ,从而在实际操作中实用程度也更高。

协商缓存跟强缓存不一样,强缓存不发请求到服务器, 所以有时候资源更新了浏览器还不知道,但是协商缓存会发请求到服务器 ,所以资源是否更新,服务器肯定知道。大部分web服务器都默认开启协商缓存,而且是同时启用【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】,比如apache:

如果没有协商缓存,每个到服务器的请求,就都得返回资源内容,这样服务器的性能会极差。

【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】一般都是同时启用,这是为了处理Last-Modified不可靠的情况。有一种场景需要注意:

比如,京东页面的资源请求,返回的repsonse header就只有Last-Modified,没有ETag:

协商缓存需要配合强缓存使用,上面这个截图中,除了Last-Modified这个header,还有强缓存的相关header, 因为如果不启用强缓存的话,协商缓存根本没有意义

如果资源已经被浏览器缓存下来,在缓存失效之前,再次请求时,默认会先检查是否命中强缓存,如果强缓存命中则直接读取缓存,如果强缓存没有命中则发请求到服务器检查是否命中协商缓存,如果协商缓存命中,则告诉浏览器还是可以从缓存读取,否则才从服务器返回最新的资源。其浏览器判断缓存的详细流程图,如下:

前端性能优化方法总结

0.前言

现今“前端”这个词涵盖的技术已经远不只HTML/CSS/JS了。我们先来看看数据的最长链路:

  1. 起点是浏览器地址栏输入框
  2. 浏览器缓存
  3. 浏览器网络请求
  4. 各级路由器和CDN
  5. Nginx或F5
  6. 网关缓存
  7. Node.js
  8. Node层缓存
  9. 静态文件读取/数据库读取/服务器渲染
  10. 返回响应通过各级路由器
  11. 浏览器发起预连接、预加载、子资源请求等
  12. 浏览器渲染
  13. JS执行引起更多网络请求和渲染

所以,每一个节点都是有优化空间的。

本文不会解释各个技术名词,请自行百度,同时也略过服务器硬件的优化。

1.地址栏的优化

  • 开启HSTS:如果全站都是HTTPS,使用HSTS不仅更安全,也减少一次HTTP 301跳转。
  • URL尽可能地短,包括域名。如果要做到SEO的keyword in url,那就只放重要的keyword即可。

2.网络层优化

  • 非安全性敏感页面和资源,使用HTTP协议即可,减少加解密过程的消耗。
  • 对静态资源设置HTTP缓存并使Max-Age尽可能长,减少浏览器发起的请求数。子资源如CSS、JS等可在文件名或query中加入版本号/时间戳/hash值,然后把缓存期限设到最大。
  • 开启Etag
  • 使用ServiceWorker的Cache API提前缓存资源
  • 使用dns-prefetch、preconnect、prefetch提示浏览器做预处理
  • 开启压缩,并覆盖尽可能多的mime type。Brotli优于Gzip。
  • 启用CDN。条件许可的话可自行预热或保持热度,减少CDN的回源。
  • 使用开源代码的免费CDN时,选择高速的。如果要做SEO,那选百度的为佳。
  • 启用HTTP2,在中高速网络下,单个连接完成数据传输的效率更高。
  • 减少内网的中间节点
  • 公网出口的Http Server用最高速的实现。软件是Nginx,硬件是F5等。
  • 使用服务质量高的DNS服务商,降低DNS过程的延时和错误率。
  • DNS设置较长的TTL
  • 各级服务器内存缓存设置足够大

3.Nginx配置优化

  • 足够多的进程
  • 足够大的内存缓存设置
  • 负载均衡
  • API请求,不同域名时,服务器直接支持跨域优于Nginx反向代理。
  • 关闭冗余的header信息

4.HTML/CSS/JS优化

  • 减少DOM数量和层级。确保语义清晰的前提下使用尽可能少的标签。
  • 资源懒加载、延时加载
  • 去掉log,debugger
  • 去掉埋点或使用beacon等高级方法来做埋点
  • 1KB以下的图片使用base64做内联
  • 图片预压缩,然后nginx关闭对图片做压缩。不关闭可能会更慢。
  • 图片做好不同分辨率的副本(包括缩略图),按需要选择显示。
  • 缓存后端数据。MPA可使用cookie、local/session storage等方式,SPA可利用框架缓存在内存。
  • 内联CSS、JS小文件
  • JS、CSS最小化
  • 合并CSS、JS文件,减少网络请求。
  • CSS Sprite。合并多个小图到一张大图里,通过定位显示指定图片,减少网络请求
  • 在<head></head>标签内的外部JS加上async属性
  • 在靠近</body>的地方加载外部JS。这些JS不应该引起渲染更新
  • 所有文件的命名里禁止使用广告拦截插件的黑名单关键字,例如ad、adv、advertise等
  • JS使用高效的API,例如字典的遍历操作使用for...of最快。
  • 少使用第三方库,能用原生实现就用原生,或只用必须依赖的库的API
  • 自己精简第三方库,去掉用不着的部分
  • 使用CSS3动画代替JS Timer类动画
  • 无限的瀑布流,回收利用DOM节点,或只置空远离屏幕外的img src。
  • 信息流做分页加载
  • canvas:离屏渲染、缓存。
  • 合理设计DOM层次,会被JS频繁操作的地方不要引起大范围的重排版。
  • 跨整站使用的代码,抽出来单独加载。
  • 单页面应用SPA,开启分包
  • 减少资源的链式依赖
  • webp的使用
  • 合理选择浏览器的兼容范围。这会影响到js的polyfill和css的verdor prefix。

5.Node.js优化

  • 开启足够多的进程。
  • 根据UA做好浏览器判断,可在渲染结果中减少一些为了兼容性而存在的代码。记得在响应header中加上vary。
  • Java或PHP后端做的优化,用Node.js实现时也可以做。比如SQL优化,Redis、MQ的使用等。
  • 模板缓存
  • 减少、合并cookie数据
  • 静态文件直接用nginx托管

 

以上是关于☆前端优化:浏览器缓存技术介绍的主要内容,如果未能解决你的问题,请参考以下文章

启迪云技术栈 | 前端性能优化必会技术:浏览器缓存你知多少

前端缓存的理解 或者 前端数据持久化的理解(强制缓存、协商缓存)

前端技术基础:浏览器缓存

大型网站技术架构,4网站的高性能架构之Web前端性能优化

前端性能优化总结

前端性能优化方法总结