前端八股文浏览器系列:浏览器渲染前端路由前端缓存(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展示不同的页面。
单页面应用中的路由分为hash
和history
模式。
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 // 返回当前状态对象
pushState
和replaceState
可以改变URL,但不会刷新页面,浏览器也不会向服务器发送请求。
对于单页面的history模式,URL的改变只能由一下情况引起:
- 点击浏览器的前进/后退,onpopstate可以监听到
- 点击a标签
- 在JS中触发
history.pushState
或history.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缓存分为:
- 强缓存
- 协商缓存
强缓存与协商缓存的关系:
- 文件没过期,强缓存
- 文件过期了,协商缓存:问服务端文件是否改变,若没改变,还是用本地的缓存(跟强缓存读同一个文件),若改变,则重新发送网络请求
强缓存
不需要发送请求到服务器,直接读取浏览器本地缓存。 是否强缓存由Expires
、Cache-Control
、Pragma
这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-Since
与ETag/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
- 对于协商缓存:
Etag
或last-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:只在本地保存,不会自动把数据发给服务器
参考
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博客
前端缓存(浏览器缓存和http缓存)详解_前端浏览器缓存_hyupeng1006的博客-CSDN博客
以上是关于前端八股文浏览器系列:浏览器渲染前端路由前端缓存(HTTP缓存)缓存存储(HTTP缓存存储本地存储)的主要内容,如果未能解决你的问题,请参考以下文章