會(huì)用 Performance 工具,就能深入理解 Event Loop
網(wǎng)頁(yè)加載后,瀏覽器會(huì)解析 html、執(zhí)行 js、渲染 css,這些工作都是在 Event Loop 里完成的,理解了 Event Loop 就能理解網(wǎng)頁(yè)的運(yùn)行流程。
但很多人對(duì) Event Loop 的理解只是停留在概念層面,并沒(méi)看過(guò)真實(shí)的 Event Loop 是怎樣的。
其實(shí)在 Performance 工具里就可以看到,今天我們一起來(lái)看一下:
首先我們需要一個(gè)網(wǎng)頁(yè),我這里用的是 react 測(cè)試 fiber 用的網(wǎng)頁(yè):
https://claudiopro.github.io/react-fiber-vs-stack-demo/fiber.html
點(diǎn)擊 Performance 面板的 reload,錄制 3 s 的數(shù)據(jù):
其中 Main 這部分就是網(wǎng)頁(yè)的主線程,也就是執(zhí)行 Event Loop 的部分:
這塊區(qū)域包含了所有 task 執(zhí)行的流程,每個(gè) task 的調(diào)用棧,因?yàn)橄袢紵幕鹧?,所以也叫做火焰圖。
鼠標(biāo)劃到想看的部分,向下拖動(dòng),就可以放大那個(gè)區(qū)域:
左右拖動(dòng)可以調(diào)整看的位置:
展示的信息中很多種顏色,這些顏色代表著不同的含義:
灰色就代表宏任務(wù) task:
藍(lán)色的是 html 的 parse,橙色的是瀏覽器內(nèi)部的 JS:
紫色是樣式的 reflow、repaint,綠色的部分就是渲染:
其余的顏色都是用戶 JS 的執(zhí)行了,那些可以不用區(qū)分。
怎么從 Performance 中看出 Event Loop 執(zhí)行的流程呢?
我們一起來(lái)看一下:
你會(huì)發(fā)現(xiàn)每隔一段時(shí)間就會(huì)有一個(gè)這種任務(wù):
放大一下是這樣的:
執(zhí)行了 Animation Frame 的回調(diào),然后執(zhí)行了回流重繪,最后執(zhí)行渲染。
這種任務(wù)每隔 16.7 ms 就會(huì)執(zhí)行一次:
這就是網(wǎng)頁(yè)里怎么執(zhí)行渲染的。
所以說(shuō) requestAnimationFrame 的回調(diào)是在渲染前執(zhí)行的,rAF 和渲染構(gòu)成了一個(gè)宏任務(wù)。
為什么有的時(shí)候會(huì)掉幀、卡頓,就是因?yàn)樽枞匿秩镜暮耆蝿?wù)的執(zhí)行:
(在 Performance 中寬度代表時(shí)間,超過(guò) 200ms 就被認(rèn)為是 Long Task,會(huì)被標(biāo)紅)
我們做性能分析,就是要找到這些 Long Task,然后優(yōu)化掉它。
那除了 rAF 和渲染,還有哪些是宏任務(wù)呢?
看下分析的結(jié)果就知道了:
可以看到 requestIdleCallback 的回調(diào)是宏任務(wù):
垃圾回收 GC 是宏任務(wù):
requestAnimationFrame 的回調(diào)是宏任務(wù):
html 中直接執(zhí)行的 script 也是宏任務(wù):
這些需要記么?
不需要,用 Performance 工具看下就知道了。
那微任務(wù)是怎么執(zhí)行的呢?
可以看到 micro task 只是 task 的一部分,宏任務(wù)執(zhí)行完就會(huì)執(zhí)行所有的微任務(wù)。
這就是這個(gè)網(wǎng)頁(yè)的 Event Loop 執(zhí)行過(guò)程。
當(dāng)你對(duì)這些熟悉了之后,看到下面的火焰圖,你就能分析出一些東西來(lái)了:
中間比較寬的標(biāo)紅的就是 Long Task,是性能優(yōu)化的主要目標(biāo)。
一些比較窄的周期性的 Task 就是 requestAnimationFrame 回調(diào)以及 reflow、rapaint 和渲染。
比較長(zhǎng)的那個(gè)調(diào)用棧一般是遞歸,而且遞歸層數(shù)特別多。
當(dāng)你展開(kāi)看的時(shí)候,它也能展示完整的代碼運(yùn)行流程:
而如果你打斷點(diǎn)調(diào)試,只能看到其中的一個(gè)調(diào)用棧,這是用 Performance 工具分析代碼流程比 debugger 斷點(diǎn)調(diào)試更好的地方。
當(dāng)你閱讀源碼的時(shí)候,也可以通過(guò) Performance 看執(zhí)行流程的全貌,然后再 debugger 某些具體的流程。
總結(jié)
Performance 工具能夠看到網(wǎng)頁(yè)的 Event Loop 是怎么運(yùn)行的,不同的顏色代表不同的含義:
- 灰色:task
- 橙色:瀏覽器內(nèi)部的 JS
- 藍(lán)色:html parse
- 紫色:reflow、repaint
- 綠色:渲染
其余的顏色都是用戶自己的 JS。
寬度代表了執(zhí)行的時(shí)間,超過(guò) 200ms 就被任務(wù)是長(zhǎng)任務(wù),需要優(yōu)化。
長(zhǎng)度代表了調(diào)用棧深度,一般特別長(zhǎng)的都是有遞歸在。
用 Performance 工具可以分析出很多東西:
- rAF 回調(diào)和 reflow、repaint 還有渲染構(gòu)成一個(gè)宏任務(wù),每 16.7 ms 執(zhí)行一次。
- rAF 回調(diào)、rIC 回調(diào)、GC、html 中的 script 等都是宏任務(wù)
- 在任務(wù)執(zhí)行完后,瀏覽器會(huì)執(zhí)行所有微任務(wù),也就是 runAllMicroTasks 部分
Performance 可以看到代碼執(zhí)行全貌,而斷點(diǎn)調(diào)試的調(diào)用棧只能看到某一條流程。所以調(diào)試代碼的時(shí)候可以 Performance 和 Debugger 結(jié)合來(lái)看。
總之,會(huì)用 Performance 工具,你就能深入理解 Event Loop,理清網(wǎng)頁(yè)執(zhí)行的全流程。