DOM中浏览器的解析(重绘与重排)

我想请教一下,DOM中浏览器的解析(重绘与重排)
最新回答
诺贝尔可爱奖

2024-09-29 15:39:58

一 、浏览器是如何解析CSS选择器的?.mod-nav h3 span {font-size: 16px;}

css

<div class="mod-nav"><header><h3><span>标题</span></h3></header><div><ul><li><a href="#">项目一</a></li><li><a href="#">项目一</a></li><li><a href="#">项目一</a></li></ul></div></div>

1、 首先浏览器会将上面的代码渲染,然后生成一颗dom树

2、分析浏览器是如何解析选择器的。从左往右:.mod-nav > h3 > span

从 .mod-nav 开始遍历?节点 header、div

然后向各?的?节点遍历

在右侧 div 的分?中,当遍历到叶节点 a 后,发现不符合规则。则重新回溯到 ul 节点,再遍历下?个 li - a 总结: 从左往右, 找后代, 其实是在进行所有后代子树的遍历, 成本非常高 (尤其是子辈元素非常多时, 非常耗成本)

从右往左:span > h3 > .mod-nav

先找到所有的 span 节点 ,然后基于每?个 span 再向上查找 h3

由 h3 再向上查找 .mod-nav 的节点

最后触及根元素 html 结束该分?遍历

总结: 从右往左, 找祖辈, 一共一个元素, 就没有几个祖辈, 效率很高! (匹配起来快)

可以看到,从右向左的匹配规则可以在第?步时就筛选掉?量不符合条件的叶节点;?从左向右的匹配规则需要消耗大量时间在失败的查找上,这在真实页面中?棵 DOM 树的节点成百上千的情况下,这种遍历方式的效率会非常的低,根本不适合采用。

因此,浏览器遵循 “从右往左” 的规则来解析 CSS 选择器!,从而提高性能

二.浏览器是如何进行界面渲染的?大致的渲染流程

获取 HTML ?件并进?解析,生成一棵 DOM 树(DOM Tree)

解析 HTML 的同时也会解析 CSS,?成样式规则(Style Rules)

根据 DOM 树和样式规则,生成一棵渲染树(Render Tree)

进行布局(Layout)(重排),即为每个节点分配?个在屏幕上应显示的确切坐标位置

进?绘制(Paint)(重绘),遍历渲染树节点,调? GPU(图形处理器) 将元素呈现出来

三 、重绘(repaint)和重排(回流reflow)是什么?重排

重排是指部分或整个渲染树需要重新分析,并且节点的尺?需要重新计算。

表现为 重新?成布局,重新排列元素。

重绘

重绘是由于节点的?何属性发?改变,或由于样式发?改变(例如:改变元素背景?)。

表现为某些元素的外观被改变。或者重排后, 进行重新绘制!

两者的关系

重绘不?定会出现重排,重排必定会触发重绘。

每个页面至少需要一次回流+重绘。(初始化渲染)

重排和重绘的代价都很?昂,频繁重排重绘, 会破坏?户体验、让界面显示变迟缓。

我们需要尽可能避免频繁触发重排和重绘, 尤其是重排

四、 何时会触发重排?

重排什么时候发生?

1、添加或者删除可见的DOM元素;

2、元素位置改变;

3、元素尺寸改变——边距、填充、边框、宽度和高度

4、内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变;

5、页面渲染初始化;

6、浏览器窗口尺寸改变——resize事件发生时

总结:总的来说布局发生了改变就会触发重排(回流),样式发生了改变就会触发重绘但是重绘不?定会出现重排,重排必定会触发重绘。五. 浏览器对重绘重排的优化let s = document.body.styles.padding = "2px" // 重排 + 重绘s.border = "1px solid red" // 再一次 重排 + 重绘s.color = "blue" // 再一次重绘s.backgroundColor = "#ccc" // 再一次 重绘s.fontSize = "14px" // 再一次 重排 + 重绘document.body.appendChild(document.createTextNode('abc!')) // 添加node,再一次 重排 + 重绘

从上个实例代码中可以看到几行简单的JS代码就引起了 4次重排、6次重绘。这样重排引起的花销很大,如果下面还有js操作的话都会都去重排重绘的话,浏览器就负荷不了这么大的花销,所以浏览器进行了一个优化操作

1 浏览器对于重绘和重排的优化策略:

浏览器会维护1个队列,把所有会引起重排、重绘的操作放入这个队列, 等队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会flush(刷新)队列,进行一个批处理。这样就会让多次的重排、重绘变成了一次重排重绘。

注意点

当然这种优化也不是不能改变,有时候我们想浏览器获取一些样式信息的时候,例如像下面的一些操作,保证获取结果的准确性,就会打破浏览器的这种优化策略,强制浏览器提前flush队列

offsetTop, offsetLeft, offsetWidth, offsetHeight

scrollTop/Left/Width/Height

clientTop/Left/Width/Height

请求了getComputedStyle()

六. 重绘重排角度, 我们应该如何优化页面渲染性能 ?

优化页面渲染性能的角度: 尽可能减少重绘和重排的次数

主要有几大方式来避免:

1 集中修改样式 (这样可以尽可能利用浏览器的优化机制, 一次重排重绘就完成渲染)

2 尽量避免在遍历循环中, 进行元素 offsetTop 等样式值的获取操作, 会强制浏览器刷新队列, 进行渲染

3 利用 transform 实现动画变化效果, 去代替 left top 的变换 (轮播图等)

transform变换, 只是视觉效果! 不会影响到其他盒子, 只触发了自己的重绘

4 使用文档碎片(DocumentFragment)可以用于批量处理, 创建元素

文档碎片的理解:

documentFragment是一个保存多个元素的容器对象(保存在内存)当更新其中的一个或者多个元素时,页面不会更新。

当documentFragment容器中保存的所有元素操作完毕了, 只有将其插入到页面中才会更新页,相当于是在内存的操作.vue中的虚拟dom就是利用这种特性。

// let ul = document.getElementById("box")// let fragment = document.createDocumentFragment()// for (let i = 0; i < 20; i++) {// let li = document.createElement("li")// li.innerHTML = "index: " + i// fragment.appendChild(li)// }// ul.appendChild(fragment)

七. 前端如何实现即时通讯?websocket1 、什么是即时通讯

严格意义上: HTTP协议只能做到客户端请求服务器, 服务器做出响应, 做不到让服务器主动给客户端推送消息!, 那么如果服务器数据更新了, 想要即时通知到客户端怎么办呢 ? (即时通信需求)

2 、Web前端,解决即时通讯的方案:

短轮询 (历史方案)

开个定时器, 每隔一段时间发请求 (实时性不强)

Comet - ajax长轮询(历史方案)

发送一个请求, 服务器只要数据不更新, 就一直阻塞 (服务器压力过大)

SSE

(利用了http协议, 流数据的传输, 并不是严格意义的双向通信, 无法复用连接)

WebSocket (主流)

性能和效率都高!

3、接下来逐一介绍一下这个几个方案的优缺点1、 短轮询(历史方案)

概念:短轮询就是客户端定时发送请求,获取服务器上的最新数据,不是真正的的即时通讯,可以理解成他是在模拟即时通讯。

优点: 浏览器兼容性好,实现简单,用定时器就可以。

缺点: 实时性不高,资源消耗高,存在较多无用请求,影响性能

2、 Comet - ajax长轮询(历史方案)

概念:基于短轮询的实时性,性能效果太差,所以衍生出了长轮询,他是基于Ajax来实现的,浏览器发送请求,服务器收到请求,服务器发现数据没有更新就会一直等着,等到更新了在响应

优点: 浏览器兼容性好,实现简单,用定时器就可以。

缺点: 服务器压力较大(维护?连接会消耗较多服务器资源)

3、SSE方案

概念:利用了http协议,流数据的传输,并不是严格意义的双向通讯,无法复用

优点: 基于 HTTP,无需太多改造就能使?;相比 WebSocket 要简单一些。

缺点: 基于?本传输,效率没有 WebSocket ?;基于HTTP协议, 不是严格的双向通信

4、 Websocket方案(目前的主流)

概念:这是基于 TCP 协议的全新、独?的协议,作?是在服务器和客户端之间建?实时的双向通信。 WebSocket 协议与 HTTP 协议保持兼容,但它不会融? HTTP 协议,仅作为 HTML 5 的?部分。

优点: 真正意义上的双向实时通信,性能好、延迟低

缺点: 独立于http的协议,使用的复杂度高,要对项目进行改造,引入成熟的库

原文:https://juejin.cn/post/7096305439805014030