Silencer-1984 / Summary-of-front-end-work

总结包括一切关于前端学习的东西,面试、学英语、开发规范
0 stars 0 forks source link

dom、回流 和 重绘 #17

Open Silencer-1984 opened 3 years ago

Silencer-1984 commented 3 years ago

defer和async

script 是会阻碍 HTML 解析的,只有下载好并执行完脚本才会继续解析 HTML defer 和async有一个共同点:下载此类脚本都不会阻止页面呈现(异步加载),区别在于:

为什么操作 DOM 慢

因为 DOM 是属于渲染引擎中的东西,而 JS 又是 JS 引擎中的东西。当我们通过 JS 操作 DOM 的时候,其实这个操作涉及到了两个线程之间的通信,那么势必会带来一些性能上的损耗。操作 DOM 次数一多,也就等同于一直在进行线程之间的通信,并且操作 DOM 可能还会带来重绘回流的情况,所以也就导致了性能上的问题。

插入几万个 DOM,如何实现页面不卡顿

解决问题的重点应该是如何分批次部分渲染 DOM,使用虚拟滚动,这种技术的原理就是只渲染可视区域内的内容,非可见区域的那就完全不渲染了,当用户在滚动的时候就实时去替换渲染的内容。

什么情况阻塞渲染

先渲染的前提是生成渲染树,所以 HTML 和 CSS 肯定会阻塞渲染。如果你想渲染的越快,你越应该降低一开始需要渲染的文件大小,并且扁平层级,优化选择器。

然后当浏览器在解析到 script 标签时,会暂停构建 DOM,完成后才会从暂停的地方重新开始。也就是说,如果你想首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都建议将 script 标签放在 body 标签底部的原因。

当然在当下,并不是说 script 标签必须放在底部,因为你可以给 script 标签添加 defer 或者 async 属性。

当 script 标签加上 defer 属性以后,表示该 JS 文件会并行下载,但是会放到 HTML 解析完成后顺序执行,所以对于这种情况你可以把 script 标签放在任意位置。 对于没有任何依赖的 JS 文件可以加上 async 属性,表示 JS 文件下载和解析不会阻塞渲染。

重绘(Repaint)和回流(Reflow)

重绘和回流会在我们设置节点样式时频繁出现,同时也会很大程度上影响性能。

重绘是当节点需要更改外观而不会影响布局的,比如改变 color 就叫称为重绘 回流是布局或者几何属性需要改变就称为回流。 回流必定会发生重绘,重绘不一定会引发回流。回流所需的成本比重绘高的多,改变父节点里的子节点很可能会导致父节点的一系列回流。

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

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

减少重绘和回流

使用 transform 替代 top 使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局) 不要把节点的属性值放在一个循环里当成循环里的变量

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

不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局 动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用 requestAnimationFrame CSS 选择符从右往左匹配查找,避免节点层级过多 将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点。比如对于 video 标签来说,浏览器会自动将该节点变为图层。