【架构师从入门到进阶】第四章:前端优化思路——第三节:前置资源和缓存

【架构师从入门到进阶】第四章:前端优化思路------第三节:前置资源和缓存

本篇文章我们来学习前端优化中的前置和缓存。

前置资源

第一个我们首先来学习前置。

这前置的做法呢,一般的是将一些可以在客户端处理的计算逻辑放在客户端做,比如有一些经常不变的计算。

举一个例子,以打车软件为例,比如说滴滴。滴滴中的计价规则,有一个预估价格的计算。大家可以想一下:什么车型?在哪个城市?多少公里?多少钱?这个是几乎不会有大变的一个数字,可能很长一段时间不会去变,那么预估价格作为这么一个功能,它不需要特别的准确,就是给出一个预估值嘛,那么这种计算呢,就可以放到客户端来做。就是说在软件启动的时候可以从客户端把计价规则拉过来,拉过来之后,当乘客输入起点和终点之后呢?那就可以利用拉过来的计算规则进行去计价,计完价之后直接给乘客一个展示(这只是我这么说的,但是滴滴实际上是不是这么做的呢?咱不知道)。

这么做的好处是什么呢?就是说如果你不在客户端做的话,那么可能就得需要把它发送到服务端去做。客户端有成千上万个设备,比如说高峰期全国几百万人同时打车,如果说不在客户端上做的话,每一个请求都发给服务端,让服务端计算一下,服务端的压力会很大。

如果让他在客户端计算的话,客户端是不是就能帮服务端分担一些压力?

缓存

我们接着说缓存。

这里分两个部分来讲:第一个是http缓存,第二个是客户端的缓存。

http缓存

第一步我们先来说什么是http缓存,如何做的,它有什么风险。

什么是http缓存

http缓存是指能控制客户端各级代理和各级交换机等设备对页面资源进行缓存。大家注意我所说的这一点,就是http缓存这一块是控制什么?

比如说这是一个客户端,它要去经过路由器,然后经过网络交换中心,然后经过网络中的各个节点,然后才能到达我们的真正的服务端是吧?比如说从你家出去,经过路由器交换机,包括市区的节点,城市的节点,然后什么陆地上的光纤各种乱七八糟的转化的路由器等等,最后才能到达我们的服务器,中间的节点是非常多的,我们统一把中间这些叫做中间节点。中间节点它是什么呢?指代很多东西。

那么,这些节点其实都可以做缓存的,就是我们请求过去之后,然后响应一路回来,中间的节点都是可以给它做缓存的。

http缓存如何做

而这些缓存呢,可以在请求头中控制,也可以响应头中控制。那么这个参数叫做什么呢?这个参数叫做catch-control,由这个属性来控制。这个属性呢,可以由客户端发往服务器,在header里面写这个参数;也可以在response里面的header中写这个参数。


这个catch-control,它可以由客户端发往服务端,也可以由服务端发给客户端,但是都在响应头里,两者的设置项并不相同,我们来一一列一下。

  • public:public只能在响应头里进行设置。public表示我服务端返回给客户端的一些数据,任何接收到的设备都可以缓存该资源
  • private:private也是在响应头中设置。响应头中有这个表示只能被单个用户缓存,不能共享这个缓存。什么意思呢?比如说客户端就属于单个用户,可以缓存该资源,而中间的各种交换机啊,中间节点啊,它服务于多个用户就不能缓存这个资源
  • no-catch:no-catch在请求和响应中都可以有,表示可以缓存但是不能直接用,使用缓存前必须前往服务器进行验证
  • no-store:no-store也是请求和响应中都可以有,表示任何设备不允许缓存该资源
  • max-age:还有就是有效时长的配置。你让各个节点把资源缓存了,不能让它无限制的有效,万一数据被更新了,缓存反而是一个错误的数据。那么缓存的有效期就可以通过max-age来设置,后面跟上秒数,后面单位是秒。它在请求和响应中都可以使用,表示缓存可以存活的时间。
  • s-maxage:s-maxage也是秒数,表示共享缓存在中间代理节点的存活时间,私有缓存会忽略这个配置。
  • max-stale:max-stale表示客户端愿意接收一个已经过期的资源,但是后面可以设置一个时长,表示这个资源过期了也可以用,但是过期时间不能超过我在这个里面设置的一个时长
  • min-fresh:表示客户端希望获取一个能在指定的秒内保持其最新状态的响应。就是说我设置一个秒数,你在这个规定的秒数内要给我发一个最新的数据。
  • must-revalidate:must-revalidate只在响应中有,表示资源过期后,在服务器重新验证之前,不可以使用该资源
  • proxy-revalidate:proxy-revalidate 跟前面的must-revalidate差不多,但是这个选项仅仅与共享缓存有关系,就是除了客户端和服务端之间的各种节点的缓存。
  • no-transform:no-transform表示不能对资源进行转换,典型的是不能压缩资源和图像。
  • only-if-cached:only-if-cached表示客户端只请求自己缓存的资源,而不是向服务器去请求新的资源。

缓存风险

缓存的引入也会有一些问题,典型的就是缓存更新不及时,和真正的数据不一致。在客户端或者代理服务器存在缓存的情况下,服务器的更新无法及时反馈给客用户。

这样这种情况解决的思路有两种:

  • 更改文件名
  • 使用后端验证缓存的有效性
更改文件名

第一种更改文件名。什么意思?就是说我原来请求a资源,但是a资源缓存过期了,我不知道,那么我们怎么拿到最新的a资源呢?那就是把a的名字改一下,比如说叫a1,我们就知道原来这个资源已经被改过了,拿到的肯定就不是a了,肯定是最新的。

就是说每次有更新,都会产生一个新的文件,在部署客户端的时候,就给他把文件名改掉,这样的话,他拿到的就是最新的。

我们平时做网页,网页的主入口是index.html,index.html这个文件名是固定的,而index.html当中,它关联的文件的文件名则是在打包时随机生成的。

比如说我们现在就是一个页面,这个页面中它这些东西都是引其他的文件产生的。比如说我们来看京东,这个页面引这些文件。而这些文件呢,都是在我发布这个页面的时候自动打包放到这里面的。

当这个网页被重新部署之后,这个文件名也变了,那么这个时候他刷新整个页面的时候,他就从这个已经变了的文件名,去拿新的文件出来。这样的话,我们的index.html也就焕然一新了。

使用后端验证缓存的有效性

还有一个就是后端去验证缓存的有效性。

就是说每次用缓存之前呢,先去后端验证一下缓存是不是有效,如果缓存有效,我们再用缓存,如果缓存无效了,再去服务端去取。这样的话就能保证缓存是很及时的。因为验证这个东西,它花费的数据量是比你去拿缓存要小很多的。

我们上面写过一个no-cache,no-catch就表示可以缓存但是不允许直接去用,使用缓存之前,必须要去服务端进行验证,这样虽然客户端可以缓存资源,但是必须要经过服务器的验证,它才能使用资源。

那又引申一步服务端验证资源,需通过哪些去验证这个缓存是最新的呢?可以基于两个方面啊,第一个是资源的最后修改时间,第二个是资源的版本号。

基于资源最后修改时间验证

我们来看第一个,基于资源最后修改时间验证的时候怎么验证呢?

服务端给客户端一个响应时,他在响应头中带上last-modified属性,其中写明了该资源最后被修改的时间。客户端在验证资源时,需要在请求头中增加if-modified-since,指自己缓存的资源的最后修改时间。

服务端收到请求后,如果与当前资源的最先修改时间一致,就返回一个状态码304,并不返回资源。

如果最后修改时间不一致,那么我就给你返回一个200,并且顺便把资源返回给你。

基于资源版本号的验证方式

还有一个就是基于资源版本号的验证方式。

和通过最后修改时间的验证方式类似,只是服务器会在发出资源的响应头中携带etag属性,其中写明了资源的版本号。客户端在验证资源时,在请求途中增加if-none-match,就是是不是匹配。

服务器端基于版本号进行验证,相比于last-modified更有效一些。因为一个文件会被生成多次,其实内容并没有发生变化,etag可以准确的反映文件的变化。可能修改时间变了两次,但是它的版本没变,因为内容没有修改,可能只是加了一个空格又把这个空格删掉了,文件的修改时间就变了。所以用etag会控制的更精确一些。

基于后端验证缓存的有效方式,无论如何都要进行一次前后端的交互,只是交互的过程中可能不需要返回资源,就说交互的时候你只告诉我缓存是有效还是无效就OK了,并不需要你返回资源。

客户端缓存

接着说客户端的缓存。

作为前端,除了我们前面所讲的这些之外,我们前端其实它也是一个小型的计算机。我们还可以在前端进行另外一种缓存,这种缓存是什么呢?我们可以将数据缓存在浏览器的数据库当中。

我们来看一下,看看浏览器的缓存在哪里。

Local Storage、Session Storage、indexedDB、Web SQL、 Cookies、Cache Storage,这么多缓存。

对我们来说,用户是请求发起方,而与用户交互的系统模块,我们称之为客户端。我们现在的客户端,包括我们现在用的浏览器,git的客户端,安卓iOS的客户端,甚至h5这些客户端都支持一些本地的存储。

不要以为存储只是服务端做的事情,本地也会有。那么,接下来如何充分的利用客户端的存储呢?虽然客户端一般情况下只有一个人在用,但是客户端多,如果说有成亿个客户端在帮我们扛事情,在帮我们存储数据,那得给我们服务端减少多大的压力?所以说客户端增加存储是一种十分有效的提升系统性能的方式。

用户花了几千块钱买了一个手机,就当给我们做存储,即便利了用户,也提升了用户的满意度。

有些时候不仅将数据放到客户端,还会将一些只与单一客户端有关联的,而与整体无关的操作放到客户端。就是说这个计算呢,只跟你有关系,只跟我这个用户有关系,比如上面所说的预估价格,你就计算你自己的,从哪到哪的价格,跟别人无关,那我就让客户端去计算呗,也不用到服务器了。

各种客户端缓存

  • Cookies:cookie可以设置键值对,设置过期时间,到达过期时间之后cookie还会自动失效。但是cookie有一个坏处,坏处就是浏览器会将cookie信息发送给后端,这对于缓存而言是没有必要的。作为一个缓存,你还天天在用户发请求的时候,当个拖油瓶带着,这是增会增大数据的传输压力,所以说cookie如果不必要,就不要去用它。
  • Local Storage & Session Storage:他们两个一个是存本地的,一个是存Session(会话)。它们都有相同的特点,它们是建值对的存储模块,并且有相同操作的API,都能够提供较大数据的存储,且数据不会随请求往后端发送,可以作为前端缓存使用。这两个也有区别,区别在哪里?Local Storage可以长期存储是,只要你浏览器一直在。Session Storage呢,Session Storage中存储的数据会在会话结束之后被销毁。
  • indexedDB & Web SQL:indexedDB和Web SQL这两个都是前端的数据库,采用的也是键值对存储,Web SQL是一个关系型数据库。
  • Cache Storage:Cache Storage可以缓存请求内容,以请求为键。这样有什么好处呢?就是说同一个请求,避免了多次对后端的调用。一个请求及响应存在这里面了,那我们就避免了同一个请求对后端的多次调用。

风险

那么他们有什么风险吗?

第一,这里面就别放敏感信息了是吧?比如security就别放在这了,什么用户名密码等等就别往这里面放了。

还有就是它的过期时间得考虑到,也就是缓存什么时候过期、什么时候去更新。这个过期和更新就可以参照我们在前面讲的http请求的方式,加上有效期时间,或者给他加上版本号,然后就是请求和响应中带上这个版本号,让服务端去做一些验证。比如说我要使用某一个缓存之前,我先去服务端看看这个数据是不是最新的,如果是我们就用这个,如果不是就不用。

相关推荐
明月看潮生22 分钟前
青少年编程与数学 02-006 前端开发框架VUE 22课题、状态管理
前端·javascript·vue.js·青少年编程·编程与数学
禾小毅22 分钟前
vue 实现打包并同时上传至服务器端
前端·vue.js
Front_Yue23 分钟前
Vue虚拟DOM:如何提高前端开发效率
前端·javascript·vue.js
林涧泣26 分钟前
【Uniapp-Vue3】组合式API中的组件的生命周期函数(钩子函数)
前端·javascript·uni-app
dog25030 分钟前
互联网架构变迁:从 TCP/IP “呼叫” 到 NDN “内容分发” 的逐浪之旅
网络·tcp/ip·架构
疯狂的沙粒1 小时前
对受控组件和非受控组件的理解?应用场景?
前端·javascript·react.js·前端框架
GISer_Jing1 小时前
React Fiber框架中的Render渲染阶段——workLoop(performUnitOfWork【beginWork与completeWork】)
前端·react.js·前端框架
远洋录1 小时前
Vue 开发者的 React 实战指南:性能优化篇
前端·人工智能·react
一直在学习的小白~1 小时前
基于React的两种方式使用React-pdf
前端·react.js·pdf
wfsm1 小时前
webpack03
前端·javascript·vue.js