从输入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'样式改变,此次回流往渲染队列中加入,再向下走,发现没有回流了,于是开始执行渲染队列,一次执行完毕,发生一次回流...

结尾

分享完毕~~

相关推荐
爱吃大芒果5 分钟前
Flutter for OpenHarmony 实战:mango_shop 路由系统的配置与页面跳转逻辑
开发语言·javascript·flutter
qq_177767379 分钟前
React Native鸿蒙跨平台实现消息列表用于存储所有消息数据,筛选状态用于控制消息筛选结果
javascript·react native·react.js·ecmascript·harmonyos
weixin_395448919 分钟前
main.c_cursor_0129
前端·网络·算法
沐雪架构师22 分钟前
LangChain 1.0 Agent开发实战指南
开发语言·javascript·langchain
2501_9400078934 分钟前
Flutter for OpenHarmony三国杀攻略App实战 - 战绩记录功能实现
开发语言·javascript·flutter
摘星编程38 分钟前
React Native + OpenHarmony:自定义useEllipsis省略号处理
javascript·react native·react.js
2401_859049081 小时前
git submodule update --init --recursive无法拉取解决
前端·chrome·git
这是个栗子1 小时前
【Vue代码分析】前端动态路由传参与可选参数标记:实现“添加/查看”模式的灵活路由配置
前端·javascript·vue.js
刘一说1 小时前
Vue 动态路由参数丢失问题详解:为什么 `:id` 拿不到值?
前端·javascript·vue.js
win x1 小时前
JavaSE(基础)高频面试点及 知识点
java·面试·职场和发展