网页的渲染是一个宏任务。这是我得出的结论。别急着反驳,我稍后会给出证据。先说一下什么是调试:调试就是在运行过程中的某个时刻或某个时间段,使用工具获取各种数据,帮助开发者理清逻辑、分析性能、排查问题。各种JS运行环境都会提供调试器。此外,我们还会做一些埋点报表,用于调试和统计。我们最常用的调试工具是JSDebugger,它支持断点,可以停在某处,可以查看当前上下文中的变量,调用堆栈等,对于理清逻辑很有帮助。但是性能分析的调试工具做不到这一点,也不能断断续续的实时查看,因为会影响数据的真实性。所以这种工具就是记录一段时间的数据,然后在事后做统计分析。ChromeDevtools中的性能工具比较常用。(即使是为了避免浏览器插件的影响,也需要以隐身模式运行网页)点击记录按钮记录开始记录(如果要记录从页面加载开始的数据,请点击重新加载按钮),性能会记录录音时间的各种数据。那里有什么样的数据?网页的运行有多个线程。主线程负责通过EventLoop持续执行JS和渲染。还有一些其他的线程,比如合成渲染层的线程,WebWorker的线程等等,渲染出来的每一帧都会被绘制到界面上。网页是这样运行的,那么记录的自然是这些数据:Loop过程记录了JS执行的调用栈和页面渲染过程。你看到图片中标记的小灰色块了吗?那就是一个Task,也就是宏任务。EventLoop就是循环执行宏任务。每个Task都有自己的调用栈,可以看到函数的执行路径,耗时等信息。图中的宽度代表耗时,通过block的宽度可以直观的分析性能。宏任务执行完之后,所有的微任务都会被执行,从图中也可以清楚的看到:点击各个块可以看到代码的位置,定位到对应的代码,这样就可以分析是哪块代码了性能不佳。以上就是Main线程的执行逻辑,就是通过EventLoop不断的执行JS和渲染。当然还有其他的线程,比如光栅化线程,负责将渲染出来的图层合并到一帧中。执行过程也没有秘密。说了这么多,就是想弄清楚调试工具和Performance是干什么的,记录了哪些信息。我们想知道渲染是不是一个宏观的任务,可以很容易的通过Performance来分析。我们继续看Main线程的EventLoop的执行过程:会看到灰色的小块,也就是Tasks,每隔一段时间就会执行一次。单击它,您将看到它实际上所做的是渲染。包括计算布局、更新渲染树、合并层、渲染等,这是什么意思?不是说渲染是一个宏任务吗。因此,我们得出结论:渲染是一个宏任务,通过EventLoop逐帧渲染。通过Performance调试工具,我们可以看到Main线程EventLoop的细节,看到JS执行渲染的详细过程。有时您可能会看到某些Task部分被标记为红色,并警告您这是一个LongTask。因为渲染和JS执行都是在同一个事件循环中进行的,如果有任务执行时间过长,自然会导致渲染延迟,也就是掉帧,用户会觉得该页面被卡住了。避免LongTask,这是网页性能优化的一个重点。这也是为什么React使用Fiber架构的可中断组件树渲染,代替之前递归渲染整个组件树的方式,就是为了不产生LongTasks。总结这篇文章的目的是为了证明渲染是否是一个宏任务,但其实更重要的是弄清楚调试工具的意义。调试工具可以分析程序运行过程中某个时刻或某个时间段的各方面数据。有两种方式:一种是Debugger的断点方式,可以看到上下文变量的值和调用栈,可以帮助你理解清楚逻辑,定位问题。性能分析工具则是另一种方式,通过记录一段时间的各种数据,进行事后分析统计,这样可以保证数据的真实性。网页性能分析工具Performance可以记录网页执行过程中各个线程的执行情况,主要是主线程的EventLoop的执行过程,包括JS执行、渲染等。通过Performance,我们很容易得出“渲染是一个宏观任务”的结论。就像在Debugger面前一样,JS的执行过程没有什么秘密可言。在Performance面前,网页的执行过程也不是秘密。
