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

前言

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

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

我:...

错误示范:重排就是当页面的结构发生变化了,就会重排,比如改变变字体的大小,增删 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 个点。

相关推荐
码事漫谈3 分钟前
解决 Anki 启动器下载错误的完整指南
前端
im_AMBER22 分钟前
Web 开发 27
前端·javascript·笔记·后端·学习·web
聪明的笨猪猪43 分钟前
Java Redis “缓存设计”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
蓝胖子的多啦A梦1 小时前
低版本Chrome导致弹框无法滚动的解决方案
前端·css·html·chrome浏览器·版本不同造成问题·弹框页面无法滚动
玩代码1 小时前
vue项目安装chromedriver超时解决办法
前端·javascript·vue.js
訾博ZiBo1 小时前
React 状态管理中的循环更新陷阱与解决方案
前端
StarPrayers.1 小时前
旅行商问题(TSP)(2)(heuristics.py)(TSP 的两种贪心启发式算法实现)
前端·人工智能·python·算法·pycharm·启发式算法
聪明的笨猪猪2 小时前
Java Redis “运维”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
一壶浊酒..2 小时前
ajax局部更新
前端·ajax·okhttp
苏打水com2 小时前
JavaScript 面试题标准答案模板(对应前文核心考点)
javascript·面试