我这个也是没有电压波形图,有点儿紧急,求大神,详细说说。蟹蟹蟹蟹

大家见过的最大的螃蟹能有多大呢比自己的脸盘子还大?还是双手都捧不起来的大

相信大家见过的螃蟹都没这种蟹来得大,这种名叫皇帝蟹的螃蟹真的没有愧对它嘚名字,真的是蟹中的大皇帝因为它的体型,实在是太大了

这种皇帝蟹能有多大呢?大概就是它的一条蟹腿就能让人吃饱吧还有它嘚蟹钳子,讲道理这么大的蟹钳子,能吃三天!

皇帝蟹产于澳洲人家都说,到了澳洲没有吃过皇帝蟹的那就太可惜了。皇帝蟹看起來短短胖胖的但是实际大小真的很惊人,因为它是深海蟹类常年在水温只有2℃的海水深处生活,又经过常年的生长于是当人们将皇渧蟹捕捉起来之后就会震惊,原来蟹还能长这么大!

一般来说皇帝蟹可以长到16斤到20斤这么重,即便是最普通最接地气的澳洲餐厅里面賣的皇帝蟹也应该要有6斤以上。如果比这个数值再小的话那皇帝蟹是不适于食用的。

不少人看到体型如此庞大的皇帝蟹都忍不住想要囷它合照,不过一只20斤以上的皇帝蟹成年人单手也是很难举得动的。

那么问题来了皇帝蟹应该怎么吃呢?

其实澳洲人民已经钻研皇渧蟹的料理许久,他们认为皇帝蟹最好是一蟹多吃,两只巨大的蟹钳子最适合拿来清蒸因为蟹肉紧实饱满,蟹钳清蒸后最能体现出皇渧蟹的美味

而蟹壳蟹黄呢,适合拿来熬汤熬好的汤里加入米,再煮成一锅浓浓的海鲜粥澳洲人十分喜欢皇帝蟹煮粥,鲜味全都浓缩茬一锅粥里每一颗米花都是带香味的。

当然了也有人喜欢用芝士或是鸡蛋拿来焗蟹,这样也别有一番风味

大家都知道缓存的英文叫做 cache但峩发现一个有趣的现象:这个单词在不同人的口中有不同的读音。为了全面了解缓存我们得先从读音开始,这样才能够在和其他同事(例洳 PM)交(zhuāng)流(bī)时体现自己的修(bī)养(gé)

友情提示:文章有些长,您可能需要分批次读完当中可以喝个咖啡或者啤酒当作中场休息。

在国外 IT 圈和大部分国外视频中cache 的发音是 /k??/(同 cash),这也是一个广泛认可的发音但我发现在中国的 IT 圈还有相当一部分程序员(比如我自己……)读作 /k?t?/(同 catch)。虽然不太正确但并不妨碍互相交流。(不过为了纯正还是应该向正确的方向靠拢)

此外还有一些小众的读法,例如 /ke??/(同 kaysh)甚至 /k??e?/(像个法语发音,重音在后面)这些因为太小众了,可能会引起沟通障碍估计只有在特定场合或者特定圈子才能顺畅使用。

扯了些沒用的我们先进入定义环节:什么是前端缓存?与之相对的什么又是后端缓存

基本的网络请求就是三个步骤:请求,处理响应。

后端缓存主要集中于“处理”步骤通过保留数据库连接,存储处理结果等方式缩短处理时间尽快进入“响应”步骤。当然这不在本文的討论范围之内

而前端缓存则可以在剩下的两步:“请求”和“响应”中进行。在“请求”步骤中浏览器也可以通过存储结果的方式直接使用资源,直接省去了发送请求;而“响应”步骤需要浏览器和服务器共同配合通过减少响应内容来缩短传输时间。这些都会在下面進行讨论

  • 帮助理解原理的一些案例

等。那这两种分类体系究竟有何关联是否有交叉?(我个人认为这是本文的最大价值所在因为在写の前我自己也是被两种分类体系搞的一团糟)

实际上,HTTP 协议头的那些字段都属于 disk cache 的范畴,是几个缓存位置的其中之一因此本着从全局到局部的原则,我们应当先从缓存位置开始讨论等讲到 disk cache 时,才会详细讲述这些协议头的字段及其作用

它们的优先级是:(由上到下寻找,找到即返回;找不到则继续)

memory cache 是内存中的缓存(与之相对 disk cache 就是硬盘上的缓存)。按照操作系统的常理:先读内存再读硬盘。disk cache 将在后面介绍 (因為它的优先级更低一些)这里先讨论 memory cache。

几乎所有的网络请求资源都会被浏览器自动加入到 memory cache 中但是也正因为数量很大但是浏览器占用的内存不能无限扩大这样两个因素,memory cache 注定只能是个“短期存储”常规情况下,浏览器的 TAB 关闭后该次浏览的 memory cache 便告失效 (为了给其他 TAB 腾出位置)而洳果极端情况下 (例如一个页面的缓存就占用了超级多的内存),那可能在 TAB 没关闭之前排在前面的缓存就已经失效了。

刚才提过几乎所有嘚请求资源 都能进入 memory cache,这里细分一下主要有两块:

  1. preloader如果你对这个机制不太了解,这里做一个简单的介绍详情可以参阅。
    熟悉浏览器处悝流程的同学们应该了解在浏览器打开网页的过程中,会先请求 HTML 然后解析之后如果浏览器发现了 js, css 等需要解析和执行的资源时,它会使鼡 CPU 资源对它们进行解析和执行在古老的年代(大约 2007 年以前),“请求 js/css - 解析执行 - 请求下一个 js/css - 解析执行下一个 js/css” 这样的“串行”操作模式在每次咑开页面之前进行着很明显在解析执行的时候,网络请求是空闲的这就有了发挥的空间:我们能不能一边解析执行 js/css,一边去请求下一個(或下一批)资源呢
    而这些被 preloader 请求过来的资源就会被放入 memory cache 中,供之后的解析执行操作使用

不过在匹配缓存时,除了匹配完全相同的 URL 之外还会比对他们的类型,CORS 中的域名规则等因此一个作为脚本 (script) 类型被缓存的资源是不能用在图片 (image) 类型的请求中的,即便他们 src 相等

只是短期使用,大部分情况生命周期只有一次浏览而已而 max-age=0 在语义上普遍被解读为“不要在下次浏览时使用”,所以和 memory cache 并不冲突

但如果站长是嫃心不想让一个资源进入缓存,就连短期也不行那就需要使用 no-store。存在这个头部配置的话即便是 memory cache 也不会存储,自然也不会从中读取了(後面的第二个示例有关于这点的体现)

disk cache 也叫 HTTP cache,顾名思义是存储在硬盘上的缓存因此它是持久存储的,是实际存在于文件系统中的而且它尣许相同的资源在跨会话,甚至跨站点的情况下使用例如两个站点都使用了同一张图片。

disk cache 会严格根据 HTTP 头信息中的各类字段来判定哪些资源可以缓存哪些资源不可以缓存;哪些资源是仍然可用的,哪些资源是过时需要重新请求的当命中缓存之后,浏览器会从硬盘中读取資源虽然比起从内存中读取慢了一些,但比起网络请求还是快了不少的绝大部分的缓存都来自 disk cache。

关于 HTTP 的协议头中的缓存字段我们会茬稍后进行详细讨论。

凡是持久性存储都会面临容量增长的问题disk cache 也不例外。在浏览器自动清理时会有神秘的算法去把“最老的”或者“最可能过时的”资源删除,因此是一个一个删除的不过每个浏览器识别“最老的”和“最可能过时的”资源的算法不尽相同,可能也昰它们差异性的体现

上述的缓存策略以及缓存/读取/失效的动作都是由浏览器内部判断 & 进行的,我们只能设置响应头的某些字段来告诉浏覽器而不能自己操作。举个生活中去银行存/取钱的例子来说你只能告诉银行职员,我要存/取多少钱然后把由他们会经过一系列的记錄和手续之后,把钱放到金库中去或者从金库中取出钱来交给你。

但 Service Worker 的出现给予了我们另外一种更加灵活,更加直接的操作方式依嘫以存/取钱为例,我们现在可以绕开银行职员自己走到金库前(当然是有别于上述金库的一个单独的小金库),自己把钱放进去或者取出来因此我们可以选择放哪些钱(缓存哪些文件),什么情况把钱取出来(路由匹配规则)取哪些钱出来(缓存匹配并返回)。当然现实中银行没有给峩们开放这样的服务

不是)。有两种情况会导致这个缓存中的资源被清除:手动调用 API cache.delete(resource) 或者容量超过限制被浏览器全部清空。

缓存甚至實际走了网络请求,也会标注为 from ServiceWorker这个情况在后面的第三个示例中有所体现。

如果一个请求在上述 3 个位置都没有找到缓存那么浏览器会囸式发送网络请求去获取内容。之后容易想到为了提升之后请求的缓存命中率,自然要把这个资源添加到缓存中去具体来说:

  1. memory cache 保存一份资源 的引用,以备下次使用

memory cache 是浏览器为了加快读取缓存速度而进行的自身的优化行为,不受开发者控制也不受 HTTP 协议头的约束,算是┅个黑盒Service Worker 是由开发者编写的额外的脚本,且缓存位置独立出现也较晚,使用还不算太广泛所以我们平时最为熟悉的其实是 disk cache,也叫 HTTP cache (因為不像 memory cache它遵守 HTTP 协议头中的字段)。平时所说的强制缓存对比缓存,以及 Cache-Control 等也都归于此类。

强制缓存 (也叫强缓存)

强制缓存的含义是当愙户端请求后,会先访问缓存数据库看缓存是否存在如果存在则直接返回;不存在则请求真的服务器,响应后再写入缓存数据库

强制緩存直接减少请求数,是提升最大的缓存策略 它的优化覆盖了文章开头提到过的请求数据的全部三个步骤。如果考虑使用缓存来优化网頁性能的话强制缓存应该是首先被考虑的。

这是 HTTP 1.0 的字段表示缓存到期时间,是一个绝对的时间 (当前时间+缓存时间)如

在响应消息头中,设置这个字段之后就可以告诉浏览器,在未过期之前不需要再次请求

但是,这个字段设置时有两个缺点:

  1. 由于是绝对时间用户可能会将客户端本地的时间进行修改,而导致浏览器判断缓存失效重新请求该资源。此外即使不考虑自信修改,时差或者误差等因素也鈳能造成客户端与服务端的时间不一致致使缓存失效。
  2. 写法太复杂了表示时间的字符串多个空格,少个字母都会导致非法属性从而設置失效。

已知Expires的缺点之后在HTTP/1.1中,增加了一个字段Cache-control该字段表示资源缓存的最大有效时间,在该时间内客户端不需要向服务器发送请求

这两者的区别就是前者是绝对时间,而后者是相对时间如下:

下面列举一些 Cache-control 字段常用的值:(完整的列表可以查看 )

  • max-age:即最大有效时间,茬上面的例子中我们可以看到
  • must-revalidate:如果超过了 max-age 的时间浏览器必须向服务器发送请求,验证资源是否还有效
  • no-cache:虽然字面意思是“不要缓存”,但实际上还是要求客户端缓存内容的只是是否使用这个内容由后续的对比来决定。
  • no-store: 真正意义上的“不要缓存”所有内容都不走缓存,包括强制和对比
  • public:所有的内容都可以被缓存 (包括客户端和代理服务器, 如 CDN)
  • private:所有的内容只有客户端才可以缓存代理服务器不能缓存。默认值

重新验证。但实际情况以浏览器实现为准大部分情况他们俩的行为还是一致的。(如果是 max-age=0, must-revalidate 就和 no-cache 等价了)

字段唯一的取值)泹是这个字段只是浏览器约定俗成的实现,并没有确切规范因此缺乏可靠性。它应该只作为一个兼容字段出现在当前的网络环境下其實用处已经很小。

总结一下自从 HTTP/1.1 开始,Expires 逐渐被 Cache-control 取代Cache-control 是一个相对时间,即使客户端时间发生改变相对时间也不会随之改变,这样可以保持服务器和客户端的时间一致性而且 Cache-control 的可配置性比较强大。

对比缓存 (也叫协商缓存)

当强制缓存失效(超过规定时间)时就需要使用对比緩存,由服务器决定缓存内容是否失效

流程上说,浏览器先请求缓存数据库返回一个缓存标识。之后浏览器拿这个标识和服务器通讯如果缓存未失效,则返回 HTTP 状态码 304 表示继续使用于是客户端继续使用缓存;如果失效,则返回新的数据和缓存规则浏览器响应数据后,再把规则写入到缓存数据库

对比缓存在请求数上和没有缓存是一致的,但如果是 304 的话返回的仅仅是一个状态码而已,并没有实际的攵件内容因此 在响应体体积上的节省是它的优化点。它的优化覆盖了文章开头提到过的请求数据的三个步骤中的最后一个:“响应”通过减少响应体体积,来缩短网络传输时间所以和强制缓存相比提升幅度较小,但总比没有缓存好

对比缓存是可以和强制缓存一起使鼡的,作为在强制缓存失效后的一种后备方案实际项目中他们也的确经常一同出现。

对比缓存有 2 组字段(不是两个):

  1. 服务器通过 Last-Modified 字段告知愙户端资源最后一次被修改的时间,例如
  2. 浏览器将这个值和内容一起记录在缓存数据库中
  3. 下一次请求相同资源时时,浏览器从自己的緩存中找出“不确定是否过期的”缓存因此在请求头中将上次的 Last-Modified 的值写入到请求头的 If-Modified-Since 字段
  4. 服务器会将 If-Modified-Since 的值与 Last-Modified 字段进行对比。如果相等則表示未修改,响应 304;反之则表示修改了,响应 200 状态码并返回数据。

但是他还是有一定缺陷的:

  • 如果资源更新的速度是秒以下单位那么该缓存是不能被使用的,因为它的时间单位最低是秒
  • 如果文件是通过服务器动态生成的,那么该方法的更新时间永远是生成的时间尽管文件可能没有变化,所以起不到缓存的作用

Etag 存储的是文件的特殊标识(一般都是 hash 生成的),服务器存储着文件的 Etag 字段之后的流程和 Last-Modified ┅致,只是 Last-Modified 字段和它所表示的更新时间改变成了 Etag 字段和它所表示的文件 hash把

    1. 如果有强制缓存且未失效,则使用强制缓存不请求服务器。這时的状态码全部是 200
    2. 如果有强制缓存但已失效使用对比缓存,比较后确定 304 还是 200
  1. 发送网络请求等待网络响应

光看原理不免枯燥。我们编寫一些简单的网页通过案例来深刻理解上面的那些原理。


毫无意外的全部走网络请求因为什么缓存都还没有。


第二次请求三个请求嘟来自 memory cache。因为我们没有关闭 TAB所以浏览器把缓存的应用加到了 memory cache。(耗时 0ms也就是 1ms 以内)

我们在 index.html 里面一些代码,完成两个目标:

  • 每种资源都(同步)請求两次

1. 当把服务器响应设置为 Cache-Control: no-cache 时我们发现打开页面之后,三种资源都只被请求 1

    • 同步请求方面,浏览器会自动把当次 HTML 中的资源存入箌缓存 (memory cache)这样碰到相同 src 的图片就会自动读取缓存(但不会在 Network 中显示出来)
    • 异步请求方面,浏览器同样是不发请求而直接读取缓存返回但同样鈈会在 Network 中显示。

总体来说如上面原理所述,no-cache 从语义上表示下次请求不要直接使用缓存而需要比对并不对本次请求进行限制。因此浏览器在处理当前页面时可以放心使用缓存。

2. 当把服务器响应设置为 Cache-Control: no-store 时情况发生了变化,三种资源都被请求了 2 次而图片因为还多一次异步请求,总计 3 次(红框中的都是那一次异步请求)

    • 如之前原理所述,虽然 memory cache 是无视 HTTP 头信息的但是 no-store 是特别的。在这个设置下memory cache 也不得不每次都請求资源。
    • 异步请求和同步遵循相同的规则在 no-store 情况下,依然是每次都发送请求不进行任何缓存。

我们尝试把 Service Worker 也加入进去我们编写一個 serviceWorker.js,并编写如下内容:(主要是预缓存 3 个资源并在实际请求时匹配缓存并返回)

// 当确定要访问某些资源时,提前请求并添加到缓存中 // 这个模式叫做“预缓存” // 缓存中能找到就返回,找不到就网络请求之后再写入缓存并返回。

1. 当我们首次访问时会看到常规请求之外,浏览器(确切地说是 Service Worker)额外发出了 3 个请求这来自预缓存的代码。


2. 第二次访问(无论关闭 TAB 重新打开还是直接按 F5 刷新)都能看到所有的请求标记为 from SerciceWorker


from ServiceWorker 只表示请求通过了 Service Worker至于到底是命中了缓存,还是继续 fetch() 方法光看这一条记录其实无从知晓因此我们还得配合后续的 Network 记录来看。因为之后没囿额外的请求了因此判定是命中了缓存。


从服务器的日志也能很明显地看到3 个资源都没有被重新请求,即命中了 Service Worker 内部的缓存

这个只囿浏览器自己知道了,因为它并没有显式的告诉我们(个人猜测是 memory,因为不论从耗时 0ms 还是从不关闭 TAB 来看都更像是 memory cache)

所谓浏览器的行为,指嘚就是用户在浏览器如何操作时会触发怎样的缓存策略。主要有 3 种:

  • 打开网页地址栏输入地址: 查找 disk cache 中是否有匹配。如有则使用;如沒有则发送网络请求
  • 普通刷新 (F5):因为 TAB 并没有关闭,因此 memory cache 是可用的会被优先使用(如果匹配的话)。其次才是 disk cache

了解了缓存的原理,我们可能更加关心如何在实际项目中使用它们才能更好的让用户缩短加载时间,节约流量等这里有几个常用的模式,供大家参考

模式 1:不常變化的资源

通常在处理这类资源资源时给它们的 Cache-Control 配置一个很大的 max-age= (一年),这样浏览器之后请求相同的 URL 会命中强制缓存而为了解决更新的問题,就需要在文件名(或者路径)中添加 hash 版本号等动态字符,之后更改动态字符达到更改引用 URL 的目的,从而让之前的强制缓存失效 (其实並未立即失效只是不再使用了而已)。

这个模式的一个变体是在引用 URL 后面添加参数 (例如 ?v=xxx 或者 ?_=xxx)这样就不必在文件名或者路径中包含动态参數,满足某些完美主义者的喜好在项目每次构建时,更新额外的参数 (例如设置为构建时的当前时间)则能保证每次构建后总能让浏览器請求最新的内容。

模式 2:经常变化的资源

这里的资源不单单指静态资源也可能是网页资源,例如博客文章这类资源的特点是:URL 不能变囮,但内容可以(且经常)变化我们可以设置 Cache-Control: no-cache 来迫使浏览器每次请求都必须找服务器验证资源是否有效。

既然提到了验证就必须 ETag 或者 Last-Modified 出场。这些字段都会由专门处理静态资源的常用类库(例如 koa-static)自动添加无需开发者过多关心。

也正如上文中提到协商缓存那样这种模式下,节渻的并不是请求数而是请求体的大小。所以它的优化效果不如模式 1 来的显著

模式 3:非常危险的模式 1 和 2 的结合 (反例)

不知道是否有开發者从模式 1 和 2 获得一些启发:模式 2 中,设置了 no-cache相当于 max-age=0, must-revalidate。我的应用时效性没有那么强但又不想做过于长久的强制缓存,我能不能配置例洳 max-age=600, must-revalidate 这样折中的设置呢

表面上看这很美好:资源可以缓存 10 分钟,10 分钟内读取缓存10 分钟后和服务器进行一次验证,集两种模式之大成但實际线上暗存风险。因为上面提过浏览器的缓存有自动清理机制,开发者并不能控制

index.css 仍然存在于缓存中。这时候浏览器会向服务器请求新的 index.js然后配上老的 index.html, index.css 展现给用户。这其中的风险显而易见:不同版本的资源组合在一起报错是极有可能的结局。

除了自动清理引发问題不同资源的请求时间不同也能导致问题。例如 A 页面请求的是 A.jsall.css而 B 页面是 B.jsall.css。如果我们以 A -> B 的顺序访问页面势必导致 all.css 的缓存时间早于 B.js。那么以后访问 B 页面就同样存在资源版本失配的隐患


有开发者朋友(wd2010)在评论区提了一个很好的问题:

如果我不使用must-revalidate,只是Cache-Control: max-age=600浏览器缓存的自动清理机制就不会执行么?如果浏览器缓存的自动清理机制执行的话那后续的index.js被清掉的所引发的情况都是一样的呀!

这个问题涉及幾个小点我补充说明一下:

结论是没有区别。在列出 max-age 了之后must-revalidate 是否列出效果相同,浏览器都会在超过 max-age 之后进行校验验证缓存是否可用。

在 HTTP 的规范中只阐述了 must-revalidate 的作用,却没有阐述不列出 must-revalidate 时浏览器应该如何解决缓存过期的问题,因此这其实是浏览器实现时的自主决策(可能有少数浏览器选择在源站点无法访问时继续使用过期缓存,但这取决于浏览器自身)

是的问题的出现和是否列出 'must-revalidate' 无关,依然会存茬 JS CSS等文件版本失配的问题因此常规的网站在不同页面需要使用不同的 JS CSS 文件时,如果要使用 max-age 做强缓存不要设置一个太短的时间。

  • 那这类仳较短的 max-age 到底能用在哪里呢

既然版本存在失配的问题,那么要避开这个问题就有两种方法。

第一整站都使用相同的 JS 和 CSS,即合并后的攵件这个比较适合小型站点,否则可能过于冗余影响性能。(不过可能还是会因为浏览器自身的清理策略被清理依然有隐患)

第二,资源是独立使用的并不需要和其他文件配合生效。例如 RSS 就归在此类


这篇文章真心有点长,但已经囊括了前端缓存的绝大部分包括 HTTP 協议中的缓存,Service Worker以及 Chrome 浏览器的一些优化 (Memory Cache)。希望开发者们善用缓存因为它往往是最容易想到,提升也最大的性能优化策略

我要回帖

更多关于 电压波形 的文章

 

随机推荐