EventLoop = TaskQueue + RenderQueue,你看明白了吗?
2023-12-08 09:13:38 软件 233观看
摘要前言在最近的工作和学习中,有一个词总是在眼前挥之不去--EventLoop。而在之前,其实我们讲过相关的内容,Event Loop 可视化解析图片上文我们从偏JS调用机制的角度分析了,调用栈(Call Stack)/宏任务队列 (Task Queue)和微任

前言

在最近的工作和学习中,有一个词总是在眼前挥之不去--EventLoop。而在之前,其实我们讲过相关的内容,Event Loop 可视化解析EUc28资讯网——每日最新资讯28at.com

Event Loop图片EUc28资讯网——每日最新资讯28at.com

上文我们从偏JS调用机制的角度分析了,调用栈(Call Stack)/宏任务队列 (Task Queue)和微任务队列 (Microtask Queue)他们之间的关系和他们是如何协同合作的。并且,举了很多例子,用可视化的方式讲解它们如何工作的。EUc28资讯网——每日最新资讯28at.com

而今天,我们从浏览器内部的实现细节来谈谈EventLoop是如何从接受任务到渲染出对应页面的。EUc28资讯网——每日最新资讯28at.com

也就是下图中所涉及到的各个重要节点。在阅读完本文后,希望大家能对下面有一个清晰的认知。EUc28资讯网——每日最新资讯28at.com

图片图片EUc28资讯网——每日最新资讯28at.com

好了,天不早了,干点正事哇。EUc28资讯网——每日最新资讯28at.com

我们能所学到的知识点

  1. 前置知识点
  2. 事件循环(Event Loop)
  3. 任务队列/微任务队列/调用栈
  4. 在渲染队列中执行的是什么?
  5. EventLoop模型

1. 前置知识点

「前置知识点」,只是做一个概念的介绍,不会做深度解释。因为,这些概念在下面文章中会有出现,为了让行文更加的顺畅,所以将本该在文内的概念解释放到前面来。「如果大家对这些概念熟悉,可以直接忽略」同时,由于阅读我文章的群体有很多,所以有些知识点可能「我视之若珍宝,尔视只如草芥,弃之如敝履」。以下知识点,请「酌情使用」。EUc28资讯网——每日最新资讯28at.com

页面刷新术语

我们在页面是如何生成的(宏观角度)一文中提到过这些指标,这里就拿来主义了。EUc28资讯网——每日最新资讯28at.com

  • 「屏幕刷新频率」

一秒内屏幕刷新的次数(一秒内显示了多少帧的图像),单位 Hz(赫兹),如常见的 60 Hz。「刷新频率取决于硬件的固定参数」(不会变的)。EUc28资讯网——每日最新资讯28at.com

  • 「逐行扫描」
  • 显示器并不是一次性将画面显示到屏幕上,而是「从左到右边,从上到下逐行扫描」,顺序显示整屏的一个个像素点,不过这一过程快到人眼无法察觉到变化。EUc28资讯网——每日最新资讯28at.com

  • 以 60 Hz 刷新率的屏幕为例,这一过程即 1000 / 60 ≈ 16ms。EUc28资讯网——每日最新资讯28at.com

  • 当扫描完一个屏幕后,设备需要「重新回到第一行」以进入下一次的循环,此时有一段时间空隙,称为VerticalBlanking Interval(VBI)。EUc28资讯网——每日最新资讯28at.com

  • 「帧率 (Frame Rate)」EUc28资讯网——每日最新资讯28at.com

  • 表示 「GPU 在一秒内绘制操作的帧数」,如 60 fps,即每秒钟GPU最多绘制 60 帧画面。EUc28资讯网——每日最新资讯28at.com

  • 帧率是「动态变化」的,例如当画面静止时,GPU 是没有绘制操作的,屏幕刷新的还是buffer中的数据,即GPU最后操作的帧数据。EUc28资讯网——每日最新资讯28at.com

  • 「画面撕裂(tearing)」EUc28资讯网——每日最新资讯28at.com

  • 一个屏幕内的数据来自2个不同的帧,画面会出现撕裂感。EUc28资讯网——每日最新资讯28at.com

测试帧率

我们可以借助requestAnimationFrame通过每个测量前后帧发生的时间间隔,来从侧面查看本地浏览器帧率。EUc28资讯网——每日最新资讯28at.com

const checkRequestAnimationDiff = () => {    let prev;    function call() {        requestAnimationFrame((timestamp) => {            if (prev) {                console.log(timestamp - prev);                 // 应该大约是60FPS的16.6毫秒            }            prev = timestamp;            call();        });    }    call();}checkRequestAnimationDiff();

随意打开一个网站,并将上述代码贴到devtool-Console运行。EUc28资讯网——每日最新资讯28at.com

下面是,我们在React-官网[1]中实验的结果。EUc28资讯网——每日最新资讯28at.com

图片图片EUc28资讯网——每日最新资讯28at.com

从输出结果来看,虽然结果不是唯一,但是它们的值都稳定在16.67~16.68。和我们60fps是吻合的。EUc28资讯网——每日最新资讯28at.com

WebAPI

WebAPI工作的原理依赖于浏览器作为宿主环境来提供和执行这些API。在Web开发中,我们通常指的WebAPI是「浏览器内置的API」,它们允许开发者利用JavaScript与浏览器的功能进行交互。EUc28资讯网——每日最新资讯28at.com

APIsEUc28资讯网——每日最新资讯28at.com

描述EUc28资讯网——每日最新资讯28at.com

网络请求EUc28资讯网——每日最新资讯28at.com

(Network Requests)EUc28资讯网——每日最新资讯28at.com

使用XMLHttpRequestfetch API,可以发起异步的HTTP请求到服务器,并在不刷新页面的情况下获取或发送数据。EUc28资讯网——每日最新资讯28at.com


EUc28资讯网——每日最新资讯28at.com

DOM操作EUc28资讯网——每日最新资讯28at.com

(DOM Manipulation)EUc28资讯网——每日最新资讯28at.com

浏览器提供了一套DOM API,允许JavaScript访问和操作页面上的元素。比如,可以添加、删除或更改元素,或者修改元素的样式和内容。EUc28资讯网——每日最新资讯28at.com

事件处理EUc28资讯网——每日最新资讯28at.com

(Event Handling)EUc28资讯网——每日最新资讯28at.com

WebAPI允许注册事件处理程序来响应用户行为(如点击、滑动)或浏览器事件(如页面加载、窗口尺寸变化)。EUc28资讯网——每日最新资讯28at.com

存储机制EUc28资讯网——每日最新资讯28at.com

(Storage Mechanisms)EUc28资讯网——每日最新资讯28at.com

浏览器提供了如localStoragesessionStorageIndexedDB等API,可以在用户的设备上存储数据。EUc28资讯网——每日最新资讯28at.com

设备APIEUc28资讯网——每日最新资讯28at.com

(Device APIs)EUc28资讯网——每日最新资讯28at.com

可以访问设备的硬件,如摄像头、麦克风、地理位置等,这通常通过navigator对象暴露的API实现。EUc28资讯网——每日最新资讯28at.com

图形和动画EUc28资讯网——每日最新资讯28at.com

(Graphics & Animation)EUc28资讯网——每日最新资讯28at.com

CanvasWebGL API允许在网页上绘制二维和三维图形。requestAnimationFrame为动画提供了一个优化的循环机制。EUc28资讯网——每日最新资讯28at.com

性能监控EUc28资讯网——每日最新资讯28at.com

(Performance Monitoring)EUc28资讯网——每日最新资讯28at.com

Performance API提供了获取浏览器性能相关数据的接口,帮助开发者监控和优化网页性能。EUc28资讯网——每日最新资讯28at.com

其他APIEUc28资讯网——每日最新资讯28at.com

(Other APIs)EUc28资讯网——每日最新资讯28at.com

还有诸如Web Audio APIWebRTCWebSocket等,使得在网页上实现复杂的音频处理、实时通信成为可能。EUc28资讯网——每日最新资讯28at.com

WebAPI的工作流程

  1. 「调用API」:开发者在JavaScript代码中调用某个WebAPI。
  2. 「浏览器解释执行」: 浏览器解释JavaScript代码,并「执行相应的API调用」。
  3. 「API内部处理」:WebAPI内部可能会执行多种操作,如触发网络请求、访问数据库、启动硬件设备等。
  4. 「回调和事件循环」:对于异步操作,WebAPI通常会使用回调函数或Promise来处理操作完成后的结果。浏览器的事件循环机制确保了这些回调在适当的时候被调用。
  5. 「渲染和更新」:对于涉及视觉变化的API,如DOM操作或Canvas绘图,浏览器会更新页面内容,这通常发生在浏览器的下一个重绘周期。

在整个过程中,「浏览器的角色是中介」,它提供了执行API的环境和必要的安全措施。这些API让Web应用可以像本地应用一样丰富和强大,同时仍然运行在浏览器这个相对安全的沙箱环境中。EUc28资讯网——每日最新资讯28at.com

下面的图,展示了WebAPI的地位(中间部分)。EUc28资讯网——每日最新资讯28at.com

图片图片EUc28资讯网——每日最新资讯28at.com

GPU硬件加速

「GPU(Graphics Processing Unit)硬件加速」是一种利用GPU来执行图形和计算任务的技术。在Web开发中,GPU硬件加速可以通过利用用户计算机中的GPU资源来加速浏览器的渲染和绘制操作。这通常可以提高网页的性能和流畅度,尤其是对于需要大量图形操作的页面。EUc28资讯网——每日最新资讯28at.com

在Web开发中,一些CSS属性和操作可以触发GPU硬件加速,以便更有效地利用GPU资源。EUc28资讯网——每日最新资讯28at.com

  1. 3D 变换(transform)

使用transform属性进行3D变换,如translate3d、rotate3d、scale3d等,可以触发GPU硬件加速。例如:EUc28资讯网——每日最新资讯28at.com

.element {  transform: translate3d(0, 0, 0);}
  1. CSS 动画(animation)和过渡(transition):
  • 使用CSS动画和过渡属性,例如transform属性的过渡,可以触发GPU硬件加速。例如:EUc28资讯网——每日最新资讯28at.com

    .element {  transition: transform 0.3s ease-in-out;}

    EUc28资讯网——每日最新资讯28at.com

  1. EUc28资讯网——每日最新资讯28at.com

    Canvas 绘图:EUc28资讯网——每日最新资讯28at.com

    EUc28资讯网——每日最新资讯28at.com

  • 在<canvas>元素上进行绘图操作通常会利用GPU硬件加速。这包括使用2D或WebGL上下文进行图形渲染。EUc28资讯网——每日最新资讯28at.com

  1. EUc28资讯网——每日最新资讯28at.com

    使用 will-change 属性:EUc28资讯网——每日最新资讯28at.com

    EUc28资讯网——每日最新资讯28at.com

  • will-change属性告诉浏览器某个属性将会被改变,从而可以提前进行优化。例如:EUc28资讯网——每日最新资讯28at.com

    .element {  will-change: transform;}

    EUc28资讯网——每日最新资讯28at.com

  1. EUc28资讯网——每日最新资讯28at.com

    使用 image-rendering 属性:EUc28资讯网——每日最新资讯28at.com

    EUc28资讯网——每日最新资讯28at.com

  • image-rendering属性用于指定图像的渲染质量,而且在某些情况下也能触发GPU硬件加速。例如:EUc28资讯网——每日最新资讯28at.com

    .element {  image-rendering: pixelated;}

    EUc28资讯网——每日最新资讯28at.com

  1. EUc28资讯网——每日最新资讯28at.com

    使用 backface-visibility 属性:EUc28资讯网——每日最新资讯28at.com

    EUc28资讯网——每日最新资讯28at.com

  • backface-visibility属性用于指定当元素不面向屏幕时是否可见。在某些情况下,该属性的使用可以触发GPU硬件加速。例如:EUc28资讯网——每日最新资讯28at.com

    .element {  backface-visibility: hidden;}

    EUc28资讯网——每日最新资讯28at.com

  1. EUc28资讯网——每日最新资讯28at.com

    使用 filter 属性(某些情况下):EUc28资讯网——每日最新资讯28at.com

    EUc28资讯网——每日最新资讯28at.com

  • 在某些情况下,使用filter属性(如模糊、对比度等)可能触发GPU硬件加速。EUc28资讯网——每日最新资讯28at.com

还记得我们在你会在浏览器中打断点吗?我会!中介绍过如何看chromium 在线仓库[2]EUc28资讯网——每日最新资讯28at.com

那我们就从源码的角度来看看,为什么上面的属性会走GPU硬件加速EUc28资讯网——每日最新资讯28at.com

或者我们可以看compositing_reason_finder.cc这个文件,它例举了很多枚举类型。EUc28资讯网——每日最新资讯28at.com

图片图片EUc28资讯网——每日最新资讯28at.com

2. 事件循环(Event Loop)

事件循环就是一个「死循环」,不死不休。EUc28资讯网——每日最新资讯28at.com

旧的操作系统不支持多线程,它们的事件循环可以被大致描述为一个简单的循环:EUc28资讯网——每日最新资讯28at.com

while (true) {    if (hasTasks()) {        executeTask();    }}

现代操作系统的调度器(schedulers)非常复杂。它们有优先级设置、执行队列等许多其他技术。EUc28资讯网——每日最新资讯28at.com

这里做一个题外话,看到schedulers/优先级设置是不是想到React-Fiber架构了。其实,React在内部就是模仿操作系统,做了自己的实现逻辑。(这里就不展开说明了)EUc28资讯网——每日最新资讯28at.com

为了让事情简单化,我们可以将事件循环(Event Loop)描述为一个循环,该循环检查是否有任何待处理的任务:EUc28资讯网——每日最新资讯28at.com

图片图片EUc28资讯网——每日最新资讯28at.com


任务触发器

浏览器属于「事件驱动」的技术框架,如果想让Event Loop探查并执行对应的任务,首先要做的就是将某些任务进行触发。也就是唤起指定任务的触发器。EUc28资讯网——每日最新资讯28at.com

下面就是我们平时能够接触到的任务触发器EUc28资讯网——每日最新资讯28at.com

  1. 「<script>标签」:通过HTML的<script>标签引入的代码会被浏览器解析并执行,相关的同步任务会被放入事件循环中。
  2. 「延后的任务」:

setTimeout:设置一个计时器,在指定的延时后执行一段代码。EUc28资讯网——每日最新资讯28at.com

setInterval:设置一个计时器,按照指定的时间间隔重复执行一段代码。EUc28资讯网——每日最新资讯28at.com

requestIdleCallback:安排一个函数在浏览器空闲时期被调用。EUc28资讯网——每日最新资讯28at.com

  1. 「浏览器API的事件处理程序」:

用户触发的事件,例如click, mousedown, input, blur等。EUc28资讯网——每日最新资讯28at.com

代码生成的事件,比如XMLHttpRequest的响应处理、fetch API的promise resolve等。EUc28资讯网——每日最新资讯28at.com

  1. EUc28资讯网——每日最新资讯28at.com

    「Promise状态变化」:当一个Promise对象的状态改变时(例如从pending变为fulfilled或rejected),相关的任务会被加入事件循环。EUc28资讯网——每日最新资讯28at.com

    EUc28资讯网——每日最新资讯28at.com

  2. EUc28资讯网——每日最新资讯28at.com

    「观察者」:EUc28资讯网——每日最新资讯28at.com

    EUc28资讯网——每日最新资讯28at.com

DOMMutationObserver:用于观察DOM变动,当DOM发生变化时可以通知应用。EUc28资讯网——每日最新资讯28at.com

IntersectionObserver:用于观察元素是否进入了父元素或视口的特定区域。EUc28资讯网——每日最新资讯28at.com

  1. EUc28资讯网——每日最新资讯28at.com

    requestAnimationFrame:用于在「下一次重新渲染前」执行动画或视觉更新的函数,使动画流畅。EUc28资讯网——每日最新资讯28at.com

    EUc28资讯网——每日最新资讯28at.com

上面的任务几乎都是通过WebAPI进行触发的。EUc28资讯网——每日最新资讯28at.com

例如,我们在代码中有这样一行:setTimeout(function a() {}, 100)。当我们执行setTimeout时,WebAPI将任务延迟了100毫秒。100毫秒后,WebAPI将函数a()放入任务队列(Task Queue)(也可以称为Callback Queue)中。事件循环在下一个循环中获取该任务并执行它。EUc28资讯网——每日最新资讯28at.com

JS执行和页面渲染是难兄难弟

EventLoop = TaskQueue + RenderQueueEUc28资讯网——每日最新资讯28at.com

在之前的文章中,我们提到过文档对象模型(DOM)是一个应用编程接口(API),通过创建表示文档的树,以一种「独立于平台和语言」的方式访问和修改一个页面的内容和结构。EUc28资讯网——每日最新资讯28at.com

在HTML文档中,Web开发者可以使用JS来CRUD DOM 结构,其主要的目的是「动态」改变HTML文档的结构。EUc28资讯网——每日最新资讯28at.com

  • 读取DOM元素的数据:大小、属性、位置等
  • 改变属性:data-属性、宽度、高度、位置、CSS属性等
  • 创建/删除HTML节点

而在JS把玩DOM之后,就将其扔给了浏览器的渲染引擎,而渲染引擎任劳任怨的处理DOM和DOM携带的附带信息,并将其渲染成用户心仪的页面。EUc28资讯网——每日最新资讯28at.com

也就是说「JS和浏览器(渲染引擎)都能染指过DOM」。EUc28资讯网——每日最新资讯28at.com

基于上面的特定的背景,我们可以得出一个结论,执行JS和渲染页面都在同一个线程里。EUc28资讯网——每日最新资讯28at.com

这意味着事件循环包含渲染流程。而渲染流程不是单一的操作。其实,它是渲染队列:EUc28资讯网——每日最新资讯28at.com

图片图片EUc28资讯网——每日最新资讯28at.com

现在EventLoop处理链路上有两个任务源。EUc28资讯网——每日最新资讯28at.com

  1. JS任务 - SomeJsTasks
  2. 渲染任务 - RenderQueue

屏幕更新

对于浏览器来说,事件循环与帧(frames)密切相关,因为EventLoop同时执行JS代码并渲染页面。EUc28资讯网——每日最新资讯28at.com

可以将帧视为屏幕状态的快照,即用户在某一时刻看到的画面。EUc28资讯网——每日最新资讯28at.com

图片图片EUc28资讯网——每日最新资讯28at.com

我们在Chromium 最新渲染引擎--RenderingNG也介绍过frames。EUc28资讯网——每日最新资讯28at.com

图片图片EUc28资讯网——每日最新资讯28at.com

浏览器的目标是尽快显示页面更新,考虑到硬件和软件的限制:EUc28资讯网——每日最新资讯28at.com

  • 硬件限制:屏幕刷新率
  • 软件限制:操作系统设置、浏览器及其设置、节能设置等

绝大多数浏览器/操作系统支持60帧每秒(Frames Per Second,FPS)。浏览器试图以这个特定的速率更新屏幕。EUc28资讯网——每日最新资讯28at.com

当我们使用60 FPS时,这意味着浏览器在必须「渲染新帧之前有16.6毫秒的时间段来执行任务」(1000/60),而渲染新帧也会消耗时间。EUc28资讯网——每日最新资讯28at.com

3. 任务队列/微任务队列/调用栈

浏览器使用两个队列来执行我们的JS代码:EUc28资讯网——每日最新资讯28at.com

  • 任务队列(TaskQueue)或宏任务队列专用于所有事件、延迟任务等。
  • 微任务队列(Microtask Queue)用于处理 promise 回调(已解决和已拒绝),以及 MutationObserver。这个队列中的单个元素被称为 微任务。

任务队列(Task Queue)

当浏览器收到一个新任务时,它将任务放入任务队列。每个事件循环定期从任务队列中获取任务并执行它。任务完成后,「如果浏览器有时间(渲染队列没有任务),事件循环从任务队列获取另一个任务,直到渲染队列接收到任务为止」。EUc28资讯网——每日最新资讯28at.com

案例分析1

图片图片EUc28资讯网——每日最新资讯28at.com

我们有3个任务:A、B、C。事件循环获取并执行第一个任务,花费了4毫秒。然后事件循环检查其他队列(微任务队列和渲染队列),它们是空的。事件循环执行任务B,花费了12毫秒。总共两个任务使用了16毫秒。然后浏览器将任务添加到渲染队列以绘制新帧。事件循环检查渲染队列并开始执行渲染队列中的任务,它们大约花费1毫秒。完成这些操作后,事件循环返回到任务队列并执行最后一个任务C。EUc28资讯网——每日最新资讯28at.com

事件循环无法预测任务将花费多少时间。此外,事件循环「无法暂停任务来渲染帧」,因为浏览器引擎不知道该任务是否会对绘制内容有修改动作,还是任务只是为了渲染帧做了一些无关痛痒的准备工作。EUc28资讯网——每日最新资讯28at.com

在执行JS代码期间,「JS所做的所有更改并不会直接呈现给用户,而是等到宏任务和所有待处理的微任务完成后才会表现出来」。但是,此时在JS中可以获取到最新DOM的变更信息。EUc28资讯网——每日最新资讯28at.com

案例分析2

图片图片EUc28资讯网——每日最新资讯28at.com

队列中只有2个任务(A、B)。第一个任务A花费了240毫秒(无法中断)。由于60FPS意味着每16.6毫秒应该渲染一帧,所以浏览器有14帧的空窗期。当任务A结束时,事件循环执行渲染队列中的任务以绘制新帧。EUc28资讯网——每日最新资讯28at.com

「尽管我们失去了14帧,这并不意味着我们将连续渲染15帧。它只会渲染最后一帧」。EUc28资讯网——每日最新资讯28at.com

这就是当JS中有长任务执行时,会阻塞页面的渲染,如果这14帧中间操作了过多的DOM,页面中就会有一种从第一帧到第十五帧的跳动。这就是为什么我们总是要将长任务拆分成很多小任务的原因。EUc28资讯网——每日最新资讯28at.com

调用栈(Call Stack)

在Event Loop 可视化解析讲解中我们对调用栈有过介绍。EUc28资讯网——每日最新资讯28at.com

图片图片EUc28资讯网——每日最新资讯28at.com

案例分析

function D() {  debugger;  console.log('前端柒八九');}function C() {  D();}function B() {  C();  }function other() {   // 不在我们考察堆栈上下文中}function A() {  const arr = [];  while (arr.length < 2) {    arr.push(other());  }  B();}console.log(A());

将上面的代码贴到devTool-Console或者devTool-Source-Snippet中执行。这段代码将在debugger处暂停。EUc28资讯网——每日最新资讯28at.com

这里多提一嘴,关于如何在浏览器中优雅的调试断点,可以参考你会在浏览器中打断点吗?我会!EUc28资讯网——每日最新资讯28at.com

  • console.log(A());,它是调用栈的开始。
  • 然后我们进入 A 函数,并多次调用 other。在我们到达debugger之前,这个函数不会出现在调用栈中,因为它在我们到达调试器之前就结束了。

这是我们在debugger停止时调用栈的样子:EUc28资讯网——每日最新资讯28at.com

图片图片EUc28资讯网——每日最新资讯28at.com

图中(anonymous)表示全局作用域,我们没贴出来,它就是指向25行调用栈的入口的。EUc28资讯网——每日最新资讯28at.com

当调用栈为空时,「当前任务」完成。EUc28资讯网——每日最新资讯28at.com

微任务

微任务只有两个可能的来源:EUc28资讯网——每日最新资讯28at.com

  1. Promise 回调(onResolved/onRejected)
  2. MutationObserver 回调。

微任务有一个主要特征,使它们与其他任务完全不同:EUc28资讯网——每日最新资讯28at.com

一旦调用栈为空,微任务将立即执行。EUc28资讯网——每日最新资讯28at.com

微任务可以创建其他微任务,「这些微任务将在调用栈结束时执行」。每个「新的微任务都会推迟执行新的宏任务或新帧的渲染」。EUc28资讯网——每日最新资讯28at.com

案例分析

在这个例子中,微任务队列中有4个微任务:EUc28资讯网——每日最新资讯28at.com

图片图片EUc28资讯网——每日最新资讯28at.com

要执行的第一个微任务是A。A花费了200毫秒,而且我们在渲染队列中有任务。然而,它们将被推迟,因为我们仍然有3个微任务。这意味着在执行A后,事件循环将执行微任务B、C,最后是D。当「微任务队列变空时,事件循环渲染新帧」。在这个例子中,这4个微任务花费了0.5秒。在这段时间内,浏览器「UI被阻塞,不可交互」。EUc28资讯网——每日最新资讯28at.com

后续的微任务可以阻塞网站UI,使页面变得不可交互。EUc28资讯网——每日最新资讯28at.com

这个微任务特性既可能是优势也可能是劣势。例如,当 MutationObserver 根据DOM更改调用其回调时,用户在回调完成之前看不到页面上的更改。因此,我们可以有效地管理用户看到的内容。EUc28资讯网——每日最新资讯28at.com

更新后的事件循环图示:EUc28资讯网——每日最新资讯28at.com

图片图片EUc28资讯网——每日最新资讯28at.com

各自的特性

  • 调用栈是用于跟踪「正在被执行」函数的机制,而宏任务队列是用于跟踪「将要被执行」函数的机制。
  • 宏任务队列和微任务队列都是「FIFO」(先进先出)的队列结构,这些任务是「同步阻塞」的

4. 在渲染队列中执行的是什么?

其实,在浏览器中渲染页面是有很多步骤的。EUc28资讯网——每日最新资讯28at.com

同时还涉及多个进程之间的通信。这在之前的页面是如何生成的(宏观角度)有过介绍,这里就不在罗嗦了。EUc28资讯网——每日最新资讯28at.com

而今天呢,我们从浏览器渲染帧的角度来看到底发生了啥?!其实帧渲染不是一个单一的操作,它有几个阶段,每个阶段都可以分为子阶段。EUc28资讯网——每日最新资讯28at.com

新帧渲染的基本结构EUc28资讯网——每日最新资讯28at.com

RequestAnimationFrame(RAF)

图片图片EUc28资讯网——每日最新资讯28at.com

requestAnimationFrame 是一个由浏览器提供的 JavaScript API,用于在下一次「浏览器重绘之前」执行指定的函数。这个函数通常用于执行动画或其他需要高性能更新的任务,因为它会在浏览器的绘制周期内运行,以确保动画的平滑流畅。EUc28资讯网——每日最新资讯28at.com

特点

  1. 「与浏览器的重绘同步:」 requestAnimationFrame 的执行时机与浏览器的重绘周期相同,通常是每秒60次(60帧每秒),这确保了动画的流畅性。
  2. 「自动暂停:」 当用户切换到其他标签页或最小化浏览器时,requestAnimationFrame 会自动暂停,从而节省系统资源。
  3. 「避免卡顿:」 由于与浏览器的绘制同步,requestAnimationFrame 可以避免由于连续执行任务导致的卡顿和性能问题。
  4. RAF的回调有一个DOMHighResTimeStamp参数,它是自时间起源[3]以来经过的毫秒数,即文档生命周期的开始。我们不需要在回调中使用performance.now();
  5. RAF返回一个描述符(id),因此你可以使用cancelAnimationFrame取消RAF回调(就像使用setTimeout一样);
  6. 更改元素大小或读取元素属性的JS代码会强制使用requestAnimationFrame;

样式重新计算(Recalc Style)

图片图片EUc28资讯网——每日最新资讯28at.com

浏览器重新计算应用的样式。此步骤还会计算哪些媒体查询将处于活动状态。EUc28资讯网——每日最新资讯28at.com

以下操作能触发重新计算包括EUc28资讯网——每日最新资讯28at.com

  • 直接更改,比如 a.styles.left = '10px'
  • 通过CSS文件描述的更改,比如 element.classList.add('my-styles-class')。

此过程可能触发整个DOM树的整体计算也可以是局部小范围的计算过程,取决于「被改动的元素的位置」。EUc28资讯网——每日最新资讯28at.com

  • 例如,改动body元素的属性,就会发生整个DOM树的重新计算。

图片图片EUc28资讯网——每日最新资讯28at.com

将元素样式和DOM元素结合起来,就会生成Render TreeEUc28资讯网——每日最新资讯28at.com

我们可以通过devTool-Performance来检测网站在这步所花费的时间。EUc28资讯网——每日最新资讯28at.com

图片图片EUc28资讯网——每日最新资讯28at.com

React官网-样式重新计算所花费时间EUc28资讯网——每日最新资讯28at.com

布局(Layout)

计算每个「可视元素」的位置信息(距离视口的距离和元素本身大小)。并生成对应的Layout Tree。 页面上的DOM元素越多,操作就越复杂。EUc28资讯网——每日最新资讯28at.com

图片图片EUc28资讯网——每日最新资讯28at.com

React官网-布局所花费时间React官网-布局所花费时间EUc28资讯网——每日最新资讯28at.com

触发条件

对于现今「富应用」来讲,做页面中做元素的移动和变更那是家常便饭。以下操作就会触发对应的布局流程EUc28资讯网——每日最新资讯28at.com

  • 读取与元素的大小和位置相关的属性(offsetWidth、offsetLeft、getBoundingClientRect等)。
  • 写入与元素的大小和位置相关的属性,除了某些属性(例如 transform 和 will-change)。

这里,我们用一点篇幅来讲一下为何transform/will-change能跳过布局阶段,直接进入合成阶段。EUc28资讯网——每日最新资讯28at.com

在之前的Chromium 最新渲染引擎--RenderingNG我们讲过,在渲染流程的最开始其实是还有一个步骤叫做animate。EUc28资讯网——每日最新资讯28at.com

图片图片EUc28资讯网——每日最新资讯28at.com

在渲染流程的图中,用不同颜色来标识该阶段可能会被不同的线程或者进程所执行。EUc28资讯网——每日最新资讯28at.com

颜色EUc28资讯网——每日最新资讯28at.com

所在进程/线程EUc28资讯网——每日最新资讯28at.com

绿色EUc28资讯网——每日最新资讯28at.com

渲染进程中的主线程EUc28资讯网——每日最新资讯28at.com

黄色EUc28资讯网——每日最新资讯28at.com

渲染进程中的合成线程EUc28资讯网——每日最新资讯28at.com

橘色EUc28资讯网——每日最新资讯28at.com

viz进程(也叫GPU进程)EUc28资讯网——每日最新资讯28at.com

在某些阶段,可能会被多个地方所执行,所以该阶段可能存在多个颜色。EUc28资讯网——每日最新资讯28at.com

而animate存在两个颜色(绿色和黄色)也就是这一步,会在多个地方被执行。EUc28资讯网——每日最新资讯28at.com

而让animate能够在黄色阶段,也就是在合成线程中执行,就是我们使用了一些CSS3属性EUc28资讯网——每日最新资讯28at.com

  1. transform
  2. opacity
  3. filter
  4. will-change

使用了这些属性后,会跳过前面很多的步骤,例如重新计算样式/布局/重绘等阶段。并且,在合成线程进行数据转换后,开启GPU的「硬件加速」。EUc28资讯网——每日最新资讯28at.com

就这速度,你说能不快吗。EUc28资讯网——每日最新资讯28at.com

其实,上面的内容在大部分教程中,都是一种铁打不动的定律。使用了transform/will-change开启了GPU硬件加速,所以性能提升了。EUc28资讯网——每日最新资讯28at.com

强制布局

强制布局(Forced Synchronous Layout 或 Forced Reflow)是Web性能优化领域的一个术语,它指的是浏览器在能够继续「处理后续操作之前,必须完成当前的布局计算」。EUc28资讯网——每日最新资讯28at.com

当强制执行布局时,浏览器会暂停JS主线程,尽管调用栈不是空的。EUc28资讯网——每日最新资讯28at.com

有很多我们耳熟能详的操作,都会触发强制布局。EUc28资讯网——每日最新资讯28at.com

图片图片EUc28资讯网——每日最新资讯28at.com

想了解更多

本文链接:http://www.28at.com/showinfo-26-39500-0.htmlEventLoop = TaskQueue + RenderQueue,你看明白了吗?

声明:本网页内容旨在传播知识,不代表本站观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。

显示全文

上一篇:为什么不能通过GetProcAddress调用CreateWindow?

下一篇:.NET Core 3.1 升级到 .NET 8,看看都有哪些变化

最新热点