Event Loop與瀏覽器渲染

看完這兩篇:

結論:

如果在 HTML 解析過程中,解析到了某個腳本,但這個腳本被 CSS 阻塞住了或者還沒下載完,則會中斷暫存當前的解析 task,繼續執行 eventloop,(參考下方給出的event loop的圖) 網頁被渲染。

如果 JS 全部是內聯的,或者網速好,在解析到</script>時腳本全都已下載完了,則解析 task 不會被中斷,也就不會出現渲染情況了。

這種特性可以用來做頁面骨架屏(解析中斷時提早渲染頁面)

先上一段代碼

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>demo</title>
  </head>
  <body>
    <div id="app">
      <p>俺是用來測試首屏渲染的文字。</p>
    </div>
    <script src="./bundle.js"></script>
  </body>
</html>
  • <p>俺是用來測試首屏渲染的文字。<p>會渲染嗎

  • 在腳本被阻塞的時候,是會直接渲染出來的

『需要著重指出的是,這是一個漸進的過程。為達到更好的用戶體驗,呈現引擎會力求盡快將內容顯示在屏幕上。它不必等到整個 HTML 文檔解析完畢之后,就會開始構建呈現樹和設置布局。在不斷接收和處理來自網絡的其余內容的同時,呈現引擎會將部分內容解析并顯示出來。』 - 來自『瀏覽器的工作原理:新式網絡瀏覽器幕后揭秘』https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/

所以:html解析完畢之前,是可以進行繪制的,那么上述<p>標簽能渲染出來嗎?

首先 PerformancePaintTiming API

Paint Timing 提供的PerformancePaintTiming是一個提供頁面在構建過程中的“繪制”(也稱“渲染”)時間點信息的接口。“繪制”是指將渲染樹轉換為頁面上像素的過程。(可以獲取頁面繪制過程中相關事件(FP、FCP、FMP、TTI等)發生的時間)
----------------它的規范中說明:如果update the rendering實例是first-paint (FP 首屏加載衡量指標之一)那么就記錄時間戳,上報為first-paint時間。如果update the rendering實例是first-contentful-paint那么就記錄時間戳,上報為first-contentful-paint (FCP 指標之一)時間。

什么是update the rendering

  • 先上圖
eventloop-1.png

可以看到是event loop的最后一個階段
所以: 瀏覽器的首次渲染,只是老老實實的按照 eventloop 來運行而已。eventloop 第一次進行到 update the rendering 階段的時間點那就是 first-paint 的時間點了

eventloop 是怎么運行的?

eventloop 按照 task > microtask > render的順序執行,所以可以看到,微隊列是先于頁面渲染運行的。對于task來說,Html解析其實就是一種典型的task

html parser 規范中檢索 eventloop 得:

(原文很晦澀,這里為了方便理解,直接翻譯最核心的幾句:)

當解析到 </script> 時:

如果當前文檔存在阻礙 JS 執行的 CSS 或者當前的腳本 不處于 ready to be parser-executed 狀態,spin the event loop,直到不再存在阻礙 JS 執行的 CSS 且該段腳本處于 ready to be parser-executed

我們已經知道 CSS 的加載是會阻礙 JS 執行的。而腳本不處于這個 ready to be parser-executed 狀態簡單理解就是還沒下載完。如果出現這兩種情況,腳本就無法立刻執行,需要等待。此時要進行 spin the eventloop,查閱規范,該操作即為:

(簡單翻譯)

  1. 暫存此時正在執行的 task 或 microtask
  2. 暫存此時的 js 執行上下文堆棧
  3. 清空 js執行上下文堆棧
  4. 如果當前正在執行的是 task,執行 microtask checkpoint
  5. 停止執行當前的 task/microtask。繼續執行 eventloop 的主流程。
  6. 當滿足條件時,重新添加之前暫存的 task/microtask,恢復暫存的 js 執行上下文堆棧,繼續執行。

簡單的說就是讓 eventloop 中斷并暫存當前正在執行的 task/microtask,保持 eventloop 的繼續執行,待一段時間之后滿足條件了再恢復之前的 task/microtask。

那么問題就水落石出了:

如果在 HTML 解析過程中,『解析到了某個腳本,但這個腳本被 CSS 阻塞住了或者還沒下載完』,則會中斷暫存當前的解析 task,繼續執行 eventloop,網頁被渲染

如果 JS 全部是內聯的,或者網速好,在解析到</script>時腳本全都已下載完了,則解析 task 不會被中斷,也就不會出現渲染情況了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容