通過 Performance 證明,網頁的渲染是一個宏任務
網頁的渲染是一個宏任務。 這是我下的一個結論。
別著急反駁,后面我會給出證據。
我們先來聊下什么是調試:
調試是通過工具獲取運行過程中的某一時刻或某一段時間的各方面的數據,幫助開發者理清邏輯、分析性能、排查問題等。 JS 的各種運行環境都會提供調試器,除此以外我們也會自己做一些埋點上報來做調試和統計。
我們最常用的調試工具是 JS Debugger,它支持斷點,可以在某處斷住,查看當前上下文的變量、調用棧等,這對于理清邏輯很有幫助。
但是性能分析的調試工具卻不能這樣做,不能用斷住的方式實時查看,因為會影響數據的真實性。所以這類工具都是通過錄制一段時間的數據,然后作事后的統計和分析的方式,常用的是 Chrome Devtools 里的 Performance 工具。(甚至為了避免瀏覽器插件的影響,還要用無痕模式來運行網頁)
點擊錄制按鈕 record 開始錄制(如果想錄制從頁面加載開始的數據,就點擊 reload 按鈕),Performance 會記錄下錄制時間內各方面的數據。
有哪些數據呢?
網頁的運行是有多個線程的,主線程負責通過 Event Loop 的方式來不斷的執行 JS 和渲染,也有一些別的線程,比如合成渲染圖層的線程,Web Worker 的線程等,渲染的每一幀會繪制到界面上。
網頁是這樣運行的,那記錄的自然也都是這些數據:
Performance 會記錄網頁的每個線程的數據,其中最重要的是主線程,也就是圖中的 Main,這部分記錄著 Event Loop 的執行過程,記錄著 JS 執行的調用棧和頁面渲染的流程。
看到圖中標出的一個個小灰塊了么,那就是一個個 Task,也就是宏任務。Event Loop 就是循環執行宏任務。每個 Task 都有自己的調用棧,可以看到函數的執行路徑,耗時等信息。圖中寬度代表了耗時,可以直觀的通過塊的寬窄來分析性能。
執行完宏任務會執行所有的微任務,在圖中也可以清晰的看到:
點擊每一個塊可以看到代碼的位置,可以定位到對應代碼,這樣就可以分析出哪塊代碼性能不好。
這些是 Main 線程的執行邏輯,也就是通過 Event Loop 來不斷執行 JS 和渲染。
當然,還有其他線程,比如光柵化線程,也就是負責把渲染出的圖層合并成一幀的線程:
總之,就像 Debugger 面前,JS 的執行過程沒有秘密一樣,在 Performance 面前,網頁各線程的執行過程也沒有秘密。
說了這么多,就是為了講清楚調試工具和 Performance 都是干啥的,它記錄了哪些信息。
我們想知道渲染是不是一個宏任務,自然可以通過 Performance 來輕易的分析出來。
我們繼續看 Main 線程的 Event Loop 執行過程:
你會看到一個個很小的灰塊,也就是一個個 Task,每隔一段時間都會執行,點擊它,就會看到其實他做的就是渲染,包括計算布局,更新渲染樹,合并圖層、渲染等。
這說明了什么,不就說明了渲染是一個宏任務么。
所以,我們得到了結論:渲染是一個宏任務,通過 Event Loop 來做一幀幀的渲染。
通過 Performance 調試工具,我們可以看到 Main 線程 Event Loop 的細節,看到 JS 執行和渲染的詳細過程。
有時你可能會看到有的 Task 部分被標紅了,還警告說這是 Long Task。
因為渲染和 JS 執行都是在同一個 Event Loop 內做的,那如果有執行時間過長的 Task,自然會導致渲染被延后,也就是掉幀,用戶感受到的就是頁面的卡頓。
避免 Long Task,這是網頁性能優化的一個重點。這也是為什么 React 使用了 Fiber 架構的可打斷的組件樹渲染,替代掉了之前的遞歸渲染整個組件樹的方式,就是為了不產生 Long Task。
總結
本文目的為了證明渲染是不是一個宏任務,但其實更重要的是想講清楚調試工具的意義。
調試工具可以分析程序運行過程中某一刻或某一段時間的各方面的數據,有兩種方式:一種是 Debugger 那種斷點的方式,可以看到上下文變量的值、調用棧,可以幫助理清邏輯、定位問題。而性能分析工具則是另一種方式,通過錄制一段時間內的各種數據,做事后的分析和統計,這樣能保證數據的真實性。
網頁的性能分析工具 Performance 可以記錄網頁執行過程中的各個線程的執行情況,主要是主線程的 Event Loop 的執行過程,包括 JS 執行、渲染等。
通過 Performance,我們可以輕易的得出“渲染是一個宏任務”的結論。
就像在 Debugger 面前,JS 執行過程沒有秘密一樣。在 Performance 面前,網頁的執行過程也同樣沒有秘密。