浏览器渲染机制

it2024-10-14  36

浏览器渲染机制

为什么操作DOM慢

DOM属于渲染引擎中的东西,JS又是JS引擎中的东西,当我们通过JS操作DOM的时候,涉及到了两个线程之间的通信,会带来性能上的损耗操作DOM次数多,等同于一直进行线程之间的通信,操作DOM可能会带来重绘回流的情况,导致性能上的问题

解决方法

虚拟滚动(virtualized scroller) 原理:只渲染可视区内容,非可视区域完全不渲染,当用户在滚动的时候就实时去替换渲染的内容

什么情况阻塞渲染

渲染的嵌套是生成渲染树,所以 HTML 和 CSS 肯定会阻塞渲染。如果你想渲染的越快,你越应该降低一开始需要渲染的文件大小,并且扁平层级,优化选择器。然后当浏览器在解析到 script 标签时,会暂停构建 DOM,完成后才会从暂停的地方重新开始。也就是说,如果你想首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都建议将 script 标签放在 body 标签底部的原因。当然在当下,并不是说 script 标签必须放在底部,因为你可以给 script 标签添加 defer 或者 async 属性。当 script 标签加上 defer 属性以后,表示该 JS 文件会并行下载,但是会放到 HTML 解析完成后顺序执行,所以对于这种情况你可以把 script 标签放在任意位置对于没有任何依赖的 JS 文件可以加上 async 属性,表示 JS 文件下载和解析不会阻塞渲染。

重绘(Repaint)和回流(Reflow)

重绘是当节点需要更改外观而不会影响布局的,比如改变 color 就叫称为重绘

回流是布局或者几何属性需要改变就称为回流。

回流必定会发生重绘,重绘不一定会引发回流。回流所需的成本比重绘高的多,改变父节点里的子节点很可能会导致父节点的一系列回流。

以下几个动作可能会导致性能问题:

改变 window 大小改变字体添加或删除样式文字改变定位或者浮动盒模型

重绘和回流其实也和 Eventloop 有关。

当 Eventloop 执行完 Microtasks 后,会判断 document 是否需要更新,因为浏览器是 60Hz 的刷新率,每 16.6ms 才会更新一次。然后判断是否有 resize 或者 scroll 事件,有的话会去触发事件,所以 resize 和 scroll 事件也是至少 16ms 才会触发一次,并且自带节流功能。判断是否触发了 media query更新动画并且发送事件判断是否有全屏操作事件执行 requestAnimationFrame 回调执行 IntersectionObserver 回调,该方法用于判断元素是否可见,可以用于懒加载上,但是兼容性不好更新界面以上就是一帧中可能会做的事情。如果在一帧中有空闲时间,就会去执行 requestIdleCallback 回调。

减少重绘和回流

使用 transform 替代 top

<div class="test"></div> <style> .test { position: absolute; top: 10px; width: 100px; height: 100px; background: red; } </style> <script> setTimeout(() => { // 引起回流 document.querySelector('.test').style.top = '100px' }, 1000) </script>

使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局)

不要把节点的属性值放在一个循环里当成循环里的变量

for(let i = 0; i < 1000; i++) { // 获取 offsetTop 会导致回流,因为需要去获取正确的值 console.log(document.querySelector('.test').style.offsetTop) }

不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局

动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用 requestAnimationFrame

CSS 选择符从右往左匹配查找,避免节点层级过多

将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点。比如对于 video 标签来说,浏览器会自动将该节点变为图层。

设置节点为图层的方式有很多,我们可以通过以下几个常用属性可以生成新图层 will-changevideo、iframe 标签
最新回复(0)