前端小姐姐五万字面试宝典:浏览器
1.缓存
(1).按缓存位置分
- 1.Service Worker
- 1.有两种情况会导致这个缓存中的资源被清除:手动调用 API
cache.delete(resource)
或者容量超过限制,被浏览器全部清空。 - 2.如果 Service Worker 没能命中缓存,一般情况会使用
fetch()
方法继续获取资源。这时候,浏览器就去 memory cache 或者 disk cache 进行下一次找缓存的工作了。注意:经过 Service Worker 的fetch()
方法获取的资源,即便它并没有命中 Service Worker 缓存,甚至实际走了网络请求,也会标注为from ServiceWorker
。
- 1.有两种情况会导致这个缓存中的资源被清除:手动调用 API
- 2.Memory Cache:tab关闭则失效
- 1.memory cache 机制保证了一个页面中如果有两个相同的请求 (例如两个
src
相同的image
,两个href
相同的link
)都实际只会被请求最多一次,避免浪费。 - 2.在从 memory cache 获取缓存内容时,浏览器会忽视例如
max-age=0
,no-cache
等头部配置。例如页面上存在几个相同src
的图片,即便它们可能被设置为不缓存,但依然会从 memory cache 中读取。这是因为 memory cache 只是短期使用,大部分情况生命周期只有一次浏览而已。而max-age=0
在语义上普遍被解读为“不要在下次浏览时使用”,所以和 memory cache 并不冲突。 - 3.但如果站长是真心不想让一个资源进入缓存,就连短期也不行,那就需要使用
no-store
。存在这个头部配置的话,即便是 memory cache 也不会存储,自然也不会从中读取了。
- 1.memory cache 机制保证了一个页面中如果有两个相同的请求 (例如两个
- 3.Disk Cache:disk cache 会严格根据 HTTP 头信息中的各类字段来判定哪些资源可以缓存,哪些资源不可以缓存;哪些资源是仍然可用的,哪些资源是过时需要重新请求的。当命中缓存之后,浏览器会从硬盘中读取资源,虽然比起从内存中读取慢了一些,但比起网络请求还是快了不少的。绝大部分的缓存都来自 disk cache。
- 4.网络请求:如果一个请求在上述 3 个位置都没有找到缓存,那么浏览器会正式发送网络请求去获取内容。之后容易想到,为了提升之后请求的缓存命中率,自然要把这个资源添加到缓存中去。具体来说:
- 1.根据 Service Worker 中的 handler 决定是否存入 Cache Storage (额外的缓存位置)。
- 2.根据 HTTP 头部的相关字段(
Cache-control
,Pragma
等)决定是否存入 disk cache - 3.memory cache 保存一份资源 的引用,以备下次使用。
(2).按失效策略分
memory cache 是浏览器为了加快读取缓存速度而进行的自身的优化行为,不受开发者控制,也不受 HTTP 协议头的约束,算是一个黑盒。Service Worker 是由开发者编写的额外的脚本,且缓存位置独立,出现也较晚,使用还不算太广泛。所以我们平时最为熟悉的其实是 disk cache,也叫 HTTP cache (因为不像 memory cache,它遵守 HTTP 协议头中的字段)。平时所说的强制缓存(强缓存),对比缓存(协商缓存),以及
Cache-Control
等,也都归于此类。
强制缓存 (也叫强缓存)
强制缓存直接减少请求数,是提升最大的缓存策略。 它的优化覆盖了请求、处理、响应三个阶段
可以造成强制缓存的字段是 Cache-control
和 Expires
。
- Expires:
- HTTP1.0
- 由于是绝对时间,用户可能会将客户端本地的时间进行修改,而导致浏览器判断缓存失效,重新请求该资源。此外,即使不考虑自信修改,时差或者误差等因素也可能造成客户端与服务端的时间不一致,致使缓存失效。
- 写法太复杂了。表示时间的字符串多个空格,少个字母,都会导致非法属性从而设置失效
- Cache-control
- HTTP1.1
- 优先级高
max-age
:即最大有效时间must-revalidate
:如果超过了max-age
的时间,浏览器必须向服务器发送请求,验证资源是否还有效。no-cache
:虽然字面意思是“不要缓存”,但实际上还是要求客户端缓存内容的,只是是否使用这个内容由后续的对比来决定。no-store
: 真正意义上的“不要缓存”。所有内容都不走缓存,包括强制和对比。public
:所有的内容都可以被缓存 (包括客户端和代理服务器, 如 CDN)private
:所有的内容只有客户端才可以缓存,代理服务器不能缓存。默认值。
对比缓存 (协商缓存)
对比缓存在请求数上和没有缓存是一致的,但如果是 304 的话,返回的仅仅是一个状态码而已,并没有实际的文件内容,因此 在响应体体积上的节省是它的优化点。
- Last-Modified & If-Modified-Since
- 服务器通过
Last-Modified
字段告知客户端,资源最后一次被修改的时间 - 浏览器将这个值和内容一起记录在缓存数据库中。
- 下一次请求相同资源时时,浏览器从自己的缓存中找出“不确定是否过期的”缓存。因此在请求头中将上次的
Last-Modified
的值写入到请求头的If-Modified-Since
字段 - 服务器会将
If-Modified-Since
的值与Last-Modified
字段进行对比。如果相等,则表示未修改,响应 304;反之,则表示修改了,响应 200 状态码,并返回数据。 - 如果资源更新的速度是秒以下单位,那么该缓存是不能被使用的,因为它的时间单位最低是秒。
- 如果文件是通过服务器动态生成的,那么该方法的更新时间永远是生成的时间,尽管文件可能没有变化,所以起不到缓存的作用。
- 服务器通过
- Etag & If-None-Match
- Etag 的优先级高于 Last-Modified
Etag
存储的是文件的特殊标识(一般都是 hash 生成的),服务器存储着文件的Etag
字段。- 之后的流程和
Last-Modified
一致,只是Last-Modified
字段和它所表示的更新时间改变成了Etag
字段和它所表示的文件 hash,把If-Modified-Since
变成了If-None-Match
。 - 服务器同样进行比较,命中返回 304, 不命中返回新资源和 200。
(3).Ajax 解决浏览器缓存问题
- 1.在ajax发送请求前加上 anyAjaxObj.setRequestHeader("If-Modified-Since","0")。
- 2.在ajax发送请求前加上 anyAjaxObj.setRequestHeader("Cache-Control","no-cache")。
- 3.在URL后面加上一个随机数: "fresh=" + Math.random()。
- 4.在URL后面加上时间搓:"nowtime=" + new Date().getTime()。
- 5.如果是使用jQuery,直接这样就可以了 $.ajaxSetup({cache:false})。这样页面的所有ajax都会执行这条语句就是不需要保存缓存记录。
2.浏览器渲染原理
(1).Render Tree
- 不显示(
display: none
)的元素不会被生成 - 有了
RenderTree
,我们就知道了所有节点的样式,然后计算他们在页面上的大小和位置(布局),最后把节点绘制到页面上(绘制)。 - 由于浏览器使用流式布局,对
Render Tree
的计算通常只需要遍历一次就可以完成,但table
及其内部元素除外,他们可能需要多次计算,通常要花3倍于同等元素的时间,这也是为什么要避免使用table
布局的原因之一。
(2).重绘
由于节点的几何属性发生改变或者由于样式发生改变而不会影响布局的,称为重绘,例如outline
, visibility
, color
、background-color
等,重绘的代价是高昂的,因为浏览器必须验证DOM树上其他节点元素的可见性。
(3)回流
回流是布局或者几何属性需要改变就称为回流。回流是影响浏览器性能的关键因素,因为其变化涉及到部分页面(或是整个页面)的布局更新。一个元素的回流可能会导致了其所有子元素以及DOM中紧随其后的节点、祖先节点元素的随后的回流。
(4).浏览器优化
现代浏览器大多都是通过队列机制来批量更新布局,浏览器会把修改操作放在队列中,至少一个浏览器刷新(即16.6ms)才会清空队列,但当你获取布局信息的时候,队列中可能有会影响这些属性或方法返回值的操作,即使没有,浏览器也会强制清空队列,触发回流与重绘来确保返回正确的值。
主要包括以下属性或方法:
offsetTop
、offsetLeft
、offsetWidth
、offsetHeight
scrollTop
、scrollLeft
、scrollWidth
、scrollHeight
clientTop
、clientLeft
、clientWidth
、clientHeight
width
、height
getComputedStyle()
getBoundingClientRect()
所以,我们应该避免频繁的使用上述的属性,他们都会强制渲染刷新队列。
(5).减少重绘与回流
- 1.CSS
- 2.使用
transform
替代top
- 3.使用
visibility
替换display: none
,因为前者只会引起重绘,后者会引发回流(改变了布局 - 4.避免使用
table
布局,可能很小的一个小改动会造成整个table
的重新布局。 - 5.尽可能在
DOM
树的最末端改变class
,回流是不可避免的,但可以减少其影响。尽可能在DOM树的最末端改变class,可以限制了回流的范围,使其影响尽可能少的节点。 - 6.避免设置多层内联样式,CSS 选择符从右往左匹配查找,避免节点层级过多。
<div> <a> <span></span> </a> </div> <style> span { color: red; } div > a > span { color: red; } </style> 复制代码
对于第一种设置样式的方式来说,浏览器只需要找到页面中所有的
span
标签然后设置颜色,但是对于第二种设置样式的方式来说,浏览器首先需要找到所有的span
标签,然后找到span
标签上的a
标签,最后再去找到div
标签,然后给符合这种条件的span
标签设置颜色,这样的递归过程就很复杂。所以我们应该尽可能的避免写过于具体的 CSS 选择器,然后对于 HTML 来说也尽量少的添加无意义标签,保证层级扁平。 - 7.将动画效果应用到
position
属性为absolute
或fixed
的元素上,避免影响其他元素的布局,这样只是一个重绘,而不是回流,同时,控制动画速度可以选择requestAnimationFrame
,详见探讨 requestAnimationFrame。 - 8.避免使用
CSS
表达式,可能会引发回流。 - 9.将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点,例如
will-change
、video
、iframe
等标签,浏览器会自动将该节点变为图层。 - 10.CSS3 硬件加速(GPU加速),使用css3硬件加速,可以让
transform
、opacity
、filters
这些动画不会引起回流重绘 。但是对于动画的其它属性,比如background-color
这些,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。
- 2.使用
- 2.JavaScript
- 1.避免频繁操作样式,最好一次性重写
style
属性,或者将样式列表定义为class
并一次性更改class
属性。 - 2.避免频繁操作
DOM
,创建一个documentFragment
,在它上面应用所有DOM操作
,最后再把它添加到文档中。 - 3.避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
- 4.对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
- 1.避免频繁操作样式,最好一次性重写
(6).JS 什么时候解析?
<script>
- 渲染过程中,如果遇到 JS 就停止渲染,执行 JS 代码。
- 如果 JS 需要操作CSSOM,则会先让CSSOM构建完,再执行JS,最后构建DOM
<script async>
- 异步执行引入的 JavaScript,加载完成后就执行 JS,阻塞DOM
<script defer>
- 延迟执行。载入 JavaScript 文件时不阻塞 HTML 的解析,执行阶段被放到 HTML 标签解析完成之后。
作者:何时夕
链接:https://juejin.im/post/5e91b01651882573716a9b23
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。