从输入4399.com到页面渲染之间的回流和重绘(附字节面试题)

浅聊一下

小时候最喜欢玩4399小游戏了,放学回家麻溜地打开电脑开始4399之旅。那么当我输入4399.com到页面加载完毕,这之间究竟发生了点什么事呢?

输入4399.com

浏览器加载到资源

我们输入4399.com,按下回车的那一瞬间,命运的齿轮开始转动

众所周知,我们的每一个页面都是由html、css组成的,所以你可以理解以下过程:

  1. 浏览器解析html代码,生成一个DOM树
  2. 浏览器解析css代码,生成一个CSSOM树
  3. 将DOM树和CSSOM树结合,去除不可见的元素,生成 Render Tree
  4. 计算布局(回流|重排),根据Render Tree 进行布局的计算,得到每一个节点的几何信息
  5. 绘制页面(重绘),GPU根据布局信息绘制

当页面重绘完毕后,我们就可以看到4399首页页面了

在上面的流程中,我们可以看到回流和重绘,那么什么是回流,什么又是重绘?

回流

  1. 什么是回流?
  • 浏览器计算页面布局的过程叫做回流

在上面提到,在生成Render Tree 以后开始计算页面布局,此时发生的就是回流。我们的屏幕上有许多个物理发光点,在这里就是要计算要让哪个发光点亮起来,但是此时仅计算而已,要让屏幕亮起来,还得交给接下来的重绘

  1. 什么时候发生回流
  • 当一个容器的几何数学发生变更时,页面会发生回流

    1> 改变窗口的尺寸

    2> 改变元素的尺寸

    3> 增加或者删除可见元素

    4> 页面初次渲染

    5> ...

重绘

  1. 什么是重绘
  • 将已经计算好布局的容器在屏幕上展现出来

在回流中,我们已经计算好了要让哪些发光点亮起来,在重绘中进行的操作就是让发光点亮起来

  1. 什么时候发生重绘
  • 元素的非几何属性变化时,会发生重绘

    1> 修改背景颜色

    2> 修改背景图片

    3> 修改边框颜色

    4> 修改字体颜色

理解回流和重绘

回流主要是时刻注意屏幕上的几何图形的变化

例如,当屏幕上的矩形变成圆形的时候,屏幕上物理发光点的数量和位置肯定要发生变化,此时发生回流, 那么发生重绘吗?当然,物理发光点的变化肯定会发生重绘的,有些发光点要亮起来,有些要暗下去...

重绘主要是观察物理发光点的变化

此时,我们的物理发光点的位置没有改变,但是颜色改变,所以只触发了重绘,而并没有回流

总结:

回流一定重绘,重绘不一定回流

浏览器的优化策略

由于回流和重绘涉及对页面进行重新计算和绘制,因此它们可能会消耗大量的计算资源和时间。这可能导致页面加载速度变慢、页面响应性能下降,甚至导致卡顿和不流畅的用户体验。

优化策略的目的是减少不必要的回流和重绘,从而提高页面的性能和用户体验。

当代浏览器都有一个渲染队列机制,当我们改变一个元素的样式 从而导致浏览器需要回流时,这个操作会进入渲染队列,然后浏 览器继续往下执行代码,如果还有相同行为,继续进入队列,直到 下面没有样式修改,浏览器会批量化渲染队列中的回流过程,这只 发生一次回流

简而言之,就是一下把所有的回流全都存起来,直到队列满了,或者碰到一些要强制执行队列的东西才开始执行队列中的回流

哪些属性会导致渲染队列强制执行呢?

读取容器几何信息:

offsetTop,offsetLeft,offsetWidth,offsetHeight

clientWidth,clienLeft,clientHeight,clientTop

ScrollHeight,ScrollTop,ScrollWidth,ScrollLedt

以上这些属性全部都会导致浏览器的渲染队列强制执行(当渲染队列中没东西时,不触发回流)

字节面试题

以下这段代码,发生几次回流?

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
    <script>
        let el = document.getElementById('app')
        el.style.width = (el.offsetWidth + 1) + 'px'
        el.style.width = 1 + 'px'
    </script>
</body>
</html>

答案是一次回流

当运行到el.style.width = (el.offsetWidth + 1) + 'px'时,由于遇到el-offsetWidth,读取了容器的几何信息,所以强制执行渲染队列,但是此时渲染队列中啥也没有...于是不执行。

然后让el.style.width = (el.offsetWidth + 1) + 'px',此时这段回流往渲染队列里存入,继续向下走

el.style.width = 1 + 'px'样式改变,此次回流往渲染队列中加入,再向下走,发现没有回流了,于是开始执行渲染队列,一次执行完毕,发生一次回流...

结尾

分享完毕~~

相关推荐
passerby606138 分钟前
完成前端时间处理的另一块版图
前端·github·web components
掘了1 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅1 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅1 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅2 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment2 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅2 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊2 小时前
jwt介绍
前端
爱敲代码的小鱼2 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
Cobyte2 小时前
AI全栈实战:使用 Python+LangChain+Vue3 构建一个 LLM 聊天应用
前端·后端·aigc