JavaScript >> Javascript 文檔 >  >> JavaScript

什麼是非阻塞腳本?

就在幾年前,Steve Souders 將阻塞與非阻塞的概念引入了全球 Web 開發人員的通用詞典。他的重點是指出 <script> 標籤會阻止頁面的呈現以及其他資源的下載。顯然,這對您的初始頁面加載非常不利,其中單個高延遲 <script> 標籤會導致您的頁面在很長一段時間內顯示為空白。即使進行了所有這些討論,對於非阻塞腳本以及它們與並行下載的關係似乎仍然存在一些混淆。這是我試圖消除混亂的嘗試。

JavaScript 是單線程的

首先,您需要了解一點關於 JavaScript 和瀏覽器的知識。 JavaScript 基本上是單線程的,這意味著一次只能執行一個操作。此外,這個單線程實際上是在 JavaScript 執行和瀏覽器渲染之間共享的。這通常被稱為瀏覽器的 UI 線程,通常是渲染相關性能討論的重點。

瀏覽器只能在任何特定時間點執行 JavaScript 或呈現 UI(它不能同時執行兩者)。這在邏輯上是有道理的,因為 JavaScript 可能會通過移動元素或以其他方式更改內容來影響 UI,並且下次更新 UI 時,瀏覽器希望確保使用最新信息。

有了這些知識,想想當頁面下載到瀏覽器時會發生什麼。頁面在下載時已開始呈現,然後是 <script> 遇到標籤。此時,瀏覽器將無法繼續渲染,因為 JavaScript 可能會影響 UI,因此它會等待。建立 HTTP 連接,下載、解析和執行文件。只有完成後,瀏覽器才能繼續呈現頁面的其餘部分,並完全確信輸出是最新的。

並行下載

較舊的瀏覽器實際上會停止執行所有操作,包括在下載腳本時在頁面中下載其他資源。這意味著兩個 <script> 連續的標籤將導致瀏覽器等待開始下載第二個腳本,直到第一個腳本被下載並執行。較新的瀏覽器將並行下載腳本文件,然後按順序執行它們,因此第二個腳本可以在第一個完成後立即執行(有關更多信息,請閱讀 Steve 的帖子)。

並行下載不應與異步執行混淆。請記住,JavaScript 是單線程的,因此您實際上不能同時執行兩個腳本。並行下載腳本只意味著下載了兩個腳本 同時,並不是他們被執行 同時。有很大的不同。

通過一次下載多個 JavaScript 文件,您可以節省僅下載資源的時間。如果您處理的是高延遲連接而不是按順序下載腳本文件,這可能會很重要。請記住,腳本仍然按順序執行,一次只能執行一個。

非阻塞腳本

Steve 還寫了一篇關於如何以非阻塞方式加載 JavaScript 的文章(實際上,他提供了多種方法)。我推薦的基本方法是動態創建腳本節點。而不是使用

阻塞腳本意味著頁面無法繼續渲染 直到腳本:

  1. 完全下載
  2. 已解析
  3. 已執行

在許多情況下,花費時間最長的是#1。 JavaScript 的解析和執行速度非常快,尤其是在具有優化 JavaScript 引擎的較新瀏覽器中。網絡延遲和 HTTP 連接的開銷通常是該過程中最慢的部分。當以阻塞方式加載腳本時,該腳本的響應時間大致相當於瀏覽器未呈現的時間量。這是一種糟糕的用戶體驗,而這正是使用 <script> 加載外部腳本時得到的結果 標記。

使用動態腳本節點會導致以非阻塞方式下載外部 JavaScript 文件。這意味著瀏覽器在繼續渲染之前不需要等待文件下載。實際上,前一個列表中的#1(也可能是#2)不再導致 UI 線程停止。但是由於只有一個線程,所以一旦文件被下載,JavaScript 的實際執行仍然會阻塞渲染。但是,如前所述,執行通常是序列中最快的部分,因此用戶基本上不會注意到這一點(假設您沒有在該腳本中做一些瘋狂的事情)。

因此,以非阻塞方式加載腳本基本上可以釋放瀏覽器,以便在下載腳本文件時繼續渲染。這些文件的*加載*是異步完成的,但是*執行*仍然會導致UI線程阻塞一小段時間。

HTML5 異步屬性

HTML5 在 <script> 上引入了一個新屬性 名為 async 的標記 .這是一個布爾屬性(不需要值),當指定時,會導致加載腳本文件,就像您創建了一個動態腳本節點一樣。基本用法如下:

<script type="text/javascript" async src="foo.js"></script>

當支持瀏覽器時,請參閱 async 屬性(目前只有 Firefox 3.6 支持),它知道可以下載腳本文件而不阻塞渲染。與使用 JavaScript 函數進行加載相比,這確實是一種以非阻塞方式加載文件的便捷方式。

async 屬性仍然有點誤解,並且基於瀏覽器行為有一些副作用。使用 HTML 設置時,行為非常簡單,如前所述。在動態腳本節點上設置時,行為有細微的區別。 Firefox 和 Opera 保留了外部 JavaScript 文件的執行順序,因此當兩個動態腳本節點一個接一個地添加時,您可以保證腳本將按順序執行。所以在 Firefox 3.6 中,設置 async 在第一個腳本上通知瀏覽器它無需等待執行此腳本即可執行其他可能出現在它之後的腳本。當 Opera 實現此功能時,它可能會以相同的方式工作。這是創建動態腳本節點然後設置 async 的 Google Analytics 源代碼背後的明顯動機 在上面。 Internet Explorer、Safari 和 Chrome 不保留執行順序,因為腳本在檢索到它們後會立即執行,而不管它們的插入順序如何。在這些瀏覽器中,設置 async 對腳本節點沒有影響(但也不會傷害任何東西)。

async 屬性還是有點誤解,谷歌分析源代碼創建了一個動態腳本節點然後設置 async 就證明了這一點 在上面。這樣做是多餘的,因為動態腳本節點已經異步加載。 async 屬性只有在 <script> 時才真正有用 直接包含在 HTML 中。

結論

基本上有兩種方法可以實現非阻塞(也稱為異步)JavaScript 下載:動態創建腳本節點和使用 HTML5 async <script> 的屬性 標籤。將此與較新瀏覽器中的並行腳本下載功能相結合,意味著您的頁面可以花費更少的時間來完全呈現給用戶。盡量避免阻止 JavaScript 下載。

更新(2010 年 8 月 10 日): 修復了小錯別字並更新了 async 的描述 屬性以反映史蒂夫和詹姆斯的評論。


Tutorial JavaScript 教程
  1. 在 JavaScript 中復制對象的 4 種不同技術

  2. 使用 Remarkable 和 Web 組件突出顯示 Markdown 代碼

  3. javascript中的嵌套類,私有方法的繼承

  4. 修改查詢字符串而不重新加載頁面

  5. 從被拒絕的 Promise 中返回 Observable

  6. 使用 EXPRESS 和 FIREBASE 的節點 JS 中的 CRUD 操作

  7. ReactJS 應用程序中的上下文

  1. 有趣和利潤的命令行 API

  2. 使用 `arr.flatMap((f) => f ?? [])` 時如何刪除空元素但未定義?

  3. 使用您的 Chakra UI 主題序列化樣式

  4. 在數據中渲染 vue 組件

  5. JavaScript 等效於 Python 的 format() 函數?

  6. JavaScript 定時器:你需要知道的一切

  7. 2020 年數字節點

  1. 關於 JavaScript 的炒作是什麼?

  2. 如何為開源 TypeScript 項目做貢獻

  3. 在內容豐富的富文本字段中呈現鏈接的資產和條目

  4. 使用 Vue.js 路由器提供更好的用戶體驗🛣️