面试中关于重绘和重排的错误案例

前言

上次面试中,面试小姐姐问到了我关于重绘和重排的问题,不出意外,重绘是...重排是...

小姐姐:你知道重绘和重排吗?

我:...

错误示范:重排就是当页面的结构发生变化了,就会重排,比如改变变字体的大小,增删 DOM 元素这样的。重绘就是页面结构没有变化,只是外观变了,比如改了一下字体颜色、背景颜色这样的。就只会发生重绘。

当你提到以上答案,面试官可能会继续引导:那重排和重绘有什么关系吗?

此时你可能会说:重排一定会导致重绘,重绘不一定会导致重排。因为重排结构发生变化了嘛,肯定会导致重绘。

那么此时面试官可能对这道题已经不抱太大希望了,或许会继续引导你,是否知道关键渲染路径

考点

事实上,这道题目一般考察两个点:

  • 浏览器的关键渲染路径
  • 性能的优化,如何减少重绘和重排,能答出这个,需要你对浏览器的关键路径渲染有一定的理解

这里需要我们理解,我们的html、css、js代码是如何被浏览器渲染到页面上成为一个个像素点的,涉及到CRP概念(关键渲染路径)

关键渲染路径

关键渲染路径(Critical Rendering Path)是浏览器将 HTML,CSS 和 JavaScript 转换为屏幕上的像素所经历的步骤序列。优化关键渲染路径可提高渲染性能。

大致的步骤是:浏览器解析HTML代码,创建DOM,HTML也可以请求JS,反过来说JS也可以操纵DOM,HTML也包含或者请求样式,依次构建CSSOM

浏览器引擎将两者结合起来以创建 Render Tree (渲染树),Layout(布局)确定页面上所有内容的大小和位置,确定布局后,将像素 Paint (绘制)到屏幕上。

大概过程如图:

优化关键渲染路径可以缩短首次渲染的时间。了解和优化关键渲染路径对于确保重排和重绘可以每秒 60 帧的速度进行很重要。

详细过程:

生成 DOM

DOM 构建是增量的。浏览器从远程下载 Byte => 根据相应的编码 (如 utf8) 转化为字符串 => 通过 AST(Abstract Syntax Tree,抽象语法树) 解析为 Token => 生成 Node => 生成 DOM。

单个 DOM 节点以 startTag token 开始,以 endTag token 结束。节点包含有关 HTML 元素的所有相关信息。该信息是使用 token 描述的。节点根据 token 层次结构连接到 DOM 树中。

如果另一组 startTag 和 endTag token 位于一组 startTag 和 endTag 之间,则在节点内有一个节点,这就是我们定义 DOM 树层次结构的方式。

生成 CSSOM

浏览器解析 css 文件,生成 CSSOM。CSSOM 包含了页面所有的样式,也就是如何展示 DOM 的信息。

CSSOM 跟 DOM 很像,但是不同。

DOM 构造是增量的,CSSOM 却不是。CSS 是渲染阻塞的:浏览器会阻塞页面渲染直到它接收和执行了所有的 CSS。

CSS 是渲染阻塞是因为规则可以被覆盖,所以内容不能被渲染直到 CSSOM 的完成。

Render Tree

渲染树(Render Tree)包括了内容和样式:DOM 和 CSSOM 树结合为渲染树。

为了构造渲染树,浏览器检查每个节点,从 DOM 树的根节点开始,并且决定哪些 CSS 规则被添加。

渲染树只包含了可见内容(body 里的部分)。

Head(通常)不包含任何可见信息,因此不会被包含在渲染树中。如果有元素上有 display: none;,它本身和其后代都不会出现在渲染树中。

Layout 布局

当渲染树生成,此时便可以布局,决定在页面中元素的大小、尺寸、位置,显示在页面的什么位置,以及每个元素之间的相关性,但是页面的渲染在不同的机型上是不一样的,因此在布局之前所有的步骤都是一样的,只是在布局时需要对屏幕尺寸做差异化的处理。

Paint 绘制

将元素转换成实际像素,绘制在屏幕上,当页面完成了渲染树的创建并完成了布局,此时便可以绘制到屏幕上,以后只有在受影响的区域会发生重绘,此时浏览器就会优化成只需要重绘需要绘制的那个最小区域。

但是需要注意的是,绘制的过程事实上很快,因此在我们做性能的提升时,只聚焦在这一部分并不是最优解。

当我们真正理解了关键路径渲染之后,我们再来看看重排和重绘

重排(Reflow):元素的位置发生变动时发生重排,也叫回流。此时在Layout布局阶段,计算每一个元素在设备视口内的确切位置和大小。当一个元素位置发生变化时,其父元素及其后边的元素位置都可能发生变化,代价极高。

因此,当我们和面试官谈论什么是重排时,重点不在元素的大小、位置...发生变化,这只是为什么会发生重排,我们应该回答的是什么是重排:因此我们可以回答:重新计算元素在设备视口内的确切位置和大小。

重绘,我们回答的关键点就应该落到关键渲染路径中的Paint阶段,将我们的渲染树,每一个节点转换成屏幕上的实际像素点

注意我们的答题思路

是什么、为什么、怎么做,换句话说应该是是什么,有什么特点,有什么应用场景

聊完了HTML、CSS,再来聊一聊关键渲染路径中的JavaScript。

JavaScript的执行,落在渲染树生成之前,这便是为什么JavaScript加载、解析、执行这一线程,会阻塞html、css���程,如果没有做阻塞,js可以操作dom,html生成dom,js修改dom,这就乱套了。这也是为什么js中无法获取js后面的元素,因为dom还没有构建完。

因此我们可以尽量把js放在后边,在dom加载完成再去加载js

性能优化上我们能做什么?

  1. 减少 DOM 树渲染时间(譬如降低 HTML 层级、标签尽量语义化等等)。
  2. 减少 CSSOM 树渲染时间(降低选择器层级等等)。
  3. 减少 HTTP 请求次数及请求大小。
  4. 将 css 放在页面开始位置。
  5. 将 js 放在页面底部位置,并尽可能用 defer 或者 async 避免阻塞的 js 加载,确保 DOM 树生成完才会去加载 JS。
  6. 用 link 替代@import。
  7. 如果页面 css 较少,尽量使用内嵌式。
  8. 为了减少白屏时间,页面加载时先快速生成一个 DOM 树。

小结

重排和重绘是浏览器关键渲染路径上的两个节点, 浏览器的关键渲染路径就是 DOM 和 CSSOM 生成渲染树,然后根据渲染树通过一个布局(也叫 layout)步骤来确定页面上所有内容的大小和位置,确定布局后,将像素 绘制 (也叫 Paint)到屏幕上。

其中重排就是当元素的位置发生变动的时候,浏览器重新执行布局这个步骤,来重新确定页面上内容的大小和位置,确定完之后就会进行重新绘制到屏幕上,所以重排一定会导致重绘。

如果元素位置没有发生变动,仅仅只是样式发生变动,这个时候浏览器重新渲染的时候会跳过布局步骤,直接进入绘制步骤,这就是重绘,所以重绘不一定会导致重排。

对于性能问题上,减少重绘和回流感觉没有那么重要,因为优化一般情况不是很明显,不答问题也不大,更多的性能优化是在整个链路上的优化,比如性能优化标题里面的那 8 个点。

相关推荐
学不会•32 分钟前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
EasyNTS1 小时前
H.264/H.265播放器EasyPlayer.js视频流媒体播放器关于websocket1006的异常断连
javascript·h.265·h.264
活宝小娜3 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点3 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow3 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o3 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app
开心工作室_kaic4 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
刚刚好ā4 小时前
js作用域超全介绍--全局作用域、局部作用、块级作用域
前端·javascript·vue.js·vue
沉默璇年5 小时前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder5 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript