前端八股文浏览器系列:浏览器渲染前端路由前端缓存(HTTP缓存)缓存存储(HTTP缓存存储本地存储)

Posted karshey

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端八股文浏览器系列:浏览器渲染前端路由前端缓存(HTTP缓存)缓存存储(HTTP缓存存储本地存储)相关的知识,希望对你有一定的参考价值。

文章目录

本系列目录:【前端八股文】目录总结
是以《代码随想录》八股文为主的笔记。详情参考在文末。
代码随想录的博客_CSDN博客-leecode题解,ACM题目讲解,代码随想录领域博主

渲染

具体可以看:【JavaScript高级】浏览器原理:渲染引擎解析页面步骤、回流和重绘、composite合成、defer与async_karshey的博客

步骤

用户看到的页面分为两个阶段:页面内容加载完成DOMContentLoaded 和 页面资源加载完成Load 阶段。

  • DOMContentLoaded事件触发时,仅DOM加载完成
  • load事件触发时,页面上所有DOM、样式、脚本、图片都加载完成

渲染步骤大致分为以下6步:

  • 解析html,构建DOM树
  • 解析CSS,生成CSS规则树
  • 合并DOM树和CSS规则树,生成render树
  • 布局 render树(Layout/reflow),负责各元素尺寸、位置的计算
  • 绘制 render树(paint),绘制页面像素信息
  • 浏览器将各层信息发给GPU,GPU将各层合成(composite),显示在屏幕上


注意:以上步骤不一定一次性顺序完成。比如DOM树或CSS树被修改时,这些操作会重复执行。

DOM树与render树

DOM树与render树不完全对应:

  • display:none的元素在DOM树中,但不在render树中
  • visibility:hidden 的元素在render树中

回流与重绘

回流reflow:浏览器布局发生变化后,要倒回去重新渲染。这个回退过程为回流。

重绘repaint:改变某个元素的背景色、文字颜色等不影响周围和内部布局的属性时,会引发重绘。

注意:

  • display:none 会触发回流,因为它不在render树中,改变了render布局
  • visibility:hidden 只会触发重绘,它的语义是隐藏元素,元素仍然占据布局空间,只是渲染成一个空框。此属性的改变不会改变布局,因此只触发重绘。

引起回流

  • 页面的第一次渲染(初始化)
  • DOM树的变化(如:增删节点)
  • render树变化
  • 浏览器窗口resize
  • 获取元素的某些属性

现代浏览器会对回流做优化,它会等到足够数量的变化发生,再做一次批处理回流。
浏览器为了获得正确的也会提前触发回流,这样就使得浏览器的优化失效了,这些属性包括offsetLeft、offsetTop、offsetWidth、offsetHeight、 scrollTop/Left/Width/Height、clientTop/Left/Width/Height、调用了getComputedStyle()。

引起重绘

  • 引起回流必然引起重绘:回流对应render树的布局,重绘对应render树的绘制,布局在绘制之前
  • 背景色、字体颜色改变等

减少回流与重绘

  • 避免逐个修改节点样式,尽量一次性修改
  • 可以将需要多次修改DOM的元素设置为display:none,操作完再显示(display:none不再render树内,则render不会发生改变,不会引发回流重绘)
  • 避免多次读取某些属性

前端路由

路由:根据不同的URL展示不同的页面。

单页面应用中的路由分为hashhistory模式。

hash模式

  • hash模式是一种把前端路由的路径用#拼接在真实URL后面的模式
  • #后的路径发生变化时,浏览器不会重新发起请求,而会触发hashchange事件
  • hash值的变化在浏览器的历史中留下记录,可以用浏览器的回退返回到上一个hash值

优点:

  • 兼容性好
  • 实现前端路由无需服务器支持

缺点:

  • #符号,略丑

history模式

在H5之前,已经有history对象了,只能用于多个页面的跳转:

history.go(n) // n>0前进n页;n<0后退n页
history.forward() // 前进一页
history.back() // 后退一页

H5新增了两个API:

/*
  参数说明:
    state:合法的javascript对象,可以用在popstate对象中
    title:标题,基本忽略,用null
    url: 任意有效的url,将要跳转的新地址
*/
history.pushState(state, title, url) // 保留现有记录的同时,将url加到历史记录中
history.replaceState(state, title, url) // 将历史记录中的当前页面替换成url
history.state // 返回当前状态对象

pushStatereplaceState可以改变URL,但不会刷新页面,浏览器也不会向服务器发送请求。

对于单页面的history模式,URL的改变只能由一下情况引起:

  • 点击浏览器的前进/后退,onpopstate可以监听到
  • 点击a标签
  • 在JS中触发history.pushStatehistory.replaceState

两种模式对比

  • 外观:hash的url有#
  • 刷新:hash刷新会加载到地址栏对应页面(不会发送请求),history刷新会重新发送请求
  • 兼容性:hash兼容IE8,history只能到IE10
  • 服务端支持:hash及其之后的内容变化不会导致浏览器向服务器发送请求history刷新会发送请求
  • 原理:hash通过监听浏览器的onhashchange()事件,查找对应路由规则;history利用H5新增的pushState()replaceState()方法改变url
  • 记录:hash模式只有#后面的内容修改才会添加到新的记录栈,history通过pushState设置的url与当前一样也会被记录到历史记录栈

前端缓存

讲的很清晰:前端缓存(浏览器缓存和http缓存)详解_前端浏览器缓存_hyupeng1006的博客-CSDN博客

前端缓存技术方法主要分为http缓存浏览器缓存

http缓存:

  • 强缓存
  • 协商缓存

浏览器缓存:

  • storage前端数据库
  • 应用缓存

主要内容是HTTP缓存。

HTTP缓存

HTTP缓存是:可以自动保存常见文档副本的HTTP设备。当web请求抵达缓存时,若本地有“已缓存”的副本,则可以从本地存储设备提取这个文档,而非原始服务器。

HTTP缓存的优缺点:

优点

  • 减少不必要的网络传输,节约宽带
  • 更快地加载页面
  • 减少服务器负载

缺点

  • 占内存

HTTP缓存分为:

  • 强缓存
  • 协商缓存


强缓存与协商缓存的关系:

  • 文件没过期,强缓存
  • 文件过期了,协商缓存:问服务端文件是否改变,若没改变,还是用本地的缓存(跟强缓存读同一个文件),若改变,则重新发送网络请求

强缓存

不需要发送请求到服务器,直接读取浏览器本地缓存。 是否强缓存由ExpiresCache-ControlPragma 这3个Header属性控制。

Expires

Expires 的值是一个 HTTP 日期,在浏览器发起请求时,会根据系统时间和 Expires的值进行比较,如果系统时间超过了 Expires的值,缓存失效。由于和系统时间进行比较,所以当系统时间和服务器时间不一致的时候,会有缓存有效期不准的问题。Expires的优先级在三个 Header 属性中是最低的。

Cache-Control可以用的时候,Expires属性被废弃。

Cache-Control

Cache-Control 是 HTTP/1.1 中新增的属性,在请求头和响应头中都可以使用,常用的属性值如下:

  • max-age:单位是,缓存时间计算的方式是距离发起的时间的秒数,超过间隔的秒数缓存失效
  • no-cache不使用强缓存,需要与服务器验证缓存是否新鲜
  • no-store禁止使用缓存(包括协商缓存),每次都向服务器请求最新的资源
  • private:专用于个人的缓存,中间代理、CDN 等不能缓存此响应
  • public:响应可以被中间代理、CDN 等缓存
  • must-revalidate:在缓存过期前可以使用,过期后必须向服务器验证

Pragma

Pragma 只有一个属性值,就是 no-cache ,效果和 Cache-Control 中的 no-cache一致,不使用强缓存,需要与服务器验证缓存是否新鲜,在 3 个头部属性中的优先级最高

协商缓存

协商缓存,就是与服务端协商后,通过协商结果来判断是否使用本地缓存。

当浏览器的强缓存失效的时候或者请求头中设置了不走强缓存,并且在请求头中设置了If-Modified-Since 或者 If-None-Match 的时候,会将这两个属性值到服务端去验证是否命中协商缓存,如果命中了协商缓存,会返回 304 状态,加载浏览器缓存,并且响应头会设置 Last-Modified 或者 ETag 属性。

Last-Modified/If-Modified-Since

Last-Modified/If-Modified-Since的值代表的是文件的最后修改时间
第一次请求服务端会把资源的最后修改时间放到 Last-Modified响应头中。
第二次发起请求的时候,请求头会带上上一次响应头中的 Last-Modified 的时间,并放到 If-Modified-Since 请求头属性中。
服务端根据文件最后一次修改时间和 If-Modified-Since的值进行比较,如果相等,返回 304 ,并加载浏览器缓存

Last-Modified/If-Modified-Since的缺点:

  • 用文件最后修改时间来判断:若文件被修改了但文件内容不变(加空格后删掉它),那么对应的文件最后修改时间是不匹配的,缓存会失效
  • 文件修改时间的最小单位是秒:若文件的修改频率在秒级以下,文件已经发生了修改,但不会返回新文件(会错误地返回304)

ETag/If-None-Match

Last-Modified/If-Modified-Since的上述缺陷,可以用ETag/If-None-Match来弥补。

ETag:就是将原先协商缓存的比较时间戳改为比较文件指纹。文件指纹:根据文件内容计算出的唯一哈希值。文件内容改变则文件指纹改变。

ETag/If-None-Match 的值是一串 hash 码,代表的是一个资源的标识符,当服务端的文件变化的时候,它的hash码会随之改变,通过请求头中的 If-None-Match 和当前文件的 hash 值进行比较,如果相等则表示命中协商缓存。

关于ETag:

  • ETag需要计算文件指纹,这意味着服务端需要更多的计算开销若文件大,数量多,且计算频繁,那么ETag的计算会影响到服务器的性能。在这种场景下,ETag不适合
  • ETag有强验证弱验证
  • 强验证:ETag生成的哈希码深入每个字节,文件中哪怕只有一个字节改变了,哈希码都会不同。——非常消耗计算量。
  • 弱验证:提取文件的部分属性来生成哈希值。速度比强验证快,但准确率不高。会降低协商缓存的有效性

Last-Modified/If-Modified-SinceETag/If-None-Match关系

不同于cache-control是expires的完全替代方案(翻译:能用cache-control就不要用expiress)。ETag并不是last-modified的完全替代方案,而是last-modified的补充方案(翻译:项目中到底是用ETag还是last-modified完全取决于业务场景,这两个没有谁更好谁更坏)。

一般哪些文件对应哪些缓存

先说结论:有哈希值的文件名设置强缓存没有哈希值的文件(比如index.html)设置协商缓存

哈希值,如红色划线部分:

原因:一般来说,文件名中的哈希值是由webpack等工具打包完成后自动生成的。若对相同的文件再打包一次,文件名会生成不同的哈希值。不同的文件名对协商缓存来说是不同的文件。因此文件名中有哈希值的要用强缓存。

当然,文件名是否生成哈希值是可以配置的,也因不同的框架而已。

HTTP缓存总结

  • HTTP缓存:可以减少宽带流量、加快响应速度
  • 对于强缓存:优先级Pragma>cache-control>Expires
  • 对于协商缓存:Etaglast-modified,根据业务场景选择适合的方案
  • 内存中读取的缓存比从磁盘中的
  • 所有带304的资源都是协商缓存,所有标注从内存/磁盘 中读取 的资源都是强缓存

缓存存储

HTTP缓存存储

详情查看:前端缓存与本地存储_前端本地储存是缓存吗_dayTimeAffect的博客-CSDN博客

缓存在如下四个地方,查找缓存的优先级会依次从上到下匹配,若都没命中就会去请求网络资源。

  • service worker
  • Memory Cache
  • Disk Cache
  • Push Cache

service worker

  • 服务器与浏览器之间的中间人角色,若网站注册了service worker,则它可以拦截网站所有请求,如果可以使用缓存则不会将请求发给服务器,反之则发送请求到服务器
  • 若使用,则传输协议为:HTTPS
  • 完全异步:同步API(如XHR、localStorage)不能在service worker使用
  • 其生命周期与页面无关

Memory Cache

  • 内存中的缓存。

Disk Cache

  • 磁盘中的缓存。

使用哪个?

  • 大的CSS、JS文件进磁盘,反之进内存
  • 内存使用率高时,文件优先进入磁盘

本地存储

常用的有:

  • cookie
  • localStorage
  • sessionStorage

存储大小

  • cookie:,一般不超过4k
  • localStorage、sessionStorage:,一般5M或更大(取决于浏览器)

数据有效性

  • cookie:一般由服务器生成可以设置失效时间;若没有设置失效时间,关闭浏览器cookie失效,若设置了时间,cookie就会存放在硬盘里,过期失效
  • sessionStorage:浏览器窗口关闭之前有效
  • localStorage:永久有效

作用域

  • cookie、localStorage:在同源窗口中共享
  • sessionStorage:同浏览器窗口共享

通信

  • cookie:自动携带在同源的http请求中,即使不需要,cookie也会在浏览器和服务器之间来回传递;若用cookie保存过多数据会造成性能问题
  • sessionStorage:只在浏览器保存,不参与和服务器的通信
  • localStorage:只在本地保存,不会自动把数据发给服务器

参考

浏览器渲染原理与过程 - 简书 (jianshu.com)

浏览器的渲染:过程与原理 - 知乎 (zhihu.com)

10分钟看懂浏览器的渲染过程及优化 - 掘金 (juejin.cn)

【JavaScript高级】浏览器原理:渲染引擎解析页面步骤、回流和重绘、composite合成、defer与async_karshey的博客-CSDN博客

前端路由模式详解(hash和history) - 掘金 (juejin.cn)

hash和history两种模式的区别 - 琴时 - 博客园 (cnblogs.com)

【面试】前端路由hash和history的区别_sqwu的博客-CSDN博客

hash模式和history模式浅识_spark-chen的博客-CSDN博客

前端缓存详解 - 简书 (jianshu.com)

彻底弄懂前端缓存 - 掘金 (juejin.cn)

前端缓存(浏览器缓存和http缓存)详解_前端浏览器缓存_hyupeng1006的博客-CSDN博客

图解 HTTP 缓存 - 掘金 (juejin.cn)

彻底弄懂前端缓存 - 掘金 (juejin.cn)

前端缓存与本地存储 - 掘金 (juejin.cn)

以上是关于前端八股文浏览器系列:浏览器渲染前端路由前端缓存(HTTP缓存)缓存存储(HTTP缓存存储本地存储)的主要内容,如果未能解决你的问题,请参考以下文章

前端性能优化方法总结

前端-URL到页面显示的全过程

前端八股文浏览器系列:单进程与多进程浏览器进程异步事件循环同源策略输入URL回车后TCP三次挥手四次握手

前端路由(history+hash)

前端路由

前端应该懂的浏览器缓存策略