關鍵要點
瀏覽器和 Node.js 等運行環境中的 JavaScript 程序在單線程上運行。當代碼在瀏覽器標籤頁中執行時,其他所有操作都會停止:菜單命令、下載、渲染、DOM 更新甚至 GIF 動畫。
用戶很少會注意到這一點,因為處理過程以小的塊快速進行。例如:單擊一個按鈕,引發一個事件,運行一個函數,進行計算並更新 DOM。完成後,瀏覽器可以自由處理處理隊列中的下一個項目。
JavaScript 代碼無法等待某些事情發生;想像一下,如果應用程序每次發出 Ajax 請求時都凍結,那該有多麼令人沮喪。因此,JavaScript 代碼使用事件和回調來操作:指示瀏覽器或操作系統級別的進程在操作完成後且結果準備就緒時調用特定函數。
在以下示例中,當按鈕單擊事件發生時,會執行一個處理程序函數,該函數通過應用 CSS 類來設置動畫。當動畫完成時,匿名回調將刪除該類:
// 单击按钮时引发事件 document.getElementById('clickme').addEventListener('click', handleClick); // 处理按钮单击事件 function handleClick(e) { // 获取要设置动画的元素 let sprite = document.getElementById('sprite'); if (!sprite) return; // 动画结束时删除“animate”类 sprite.addEventListener('animationend', () => { sprite.classList.remove('animate'); }); // 添加“animate”类 sprite.classList.add('animate'); }
ES2015 提供了 Promise,ES2017 引入了 async/await 以簡化編碼,但在表面之下仍然使用回調。有關更多信息,請參閱“現代 JS 中的流程控制”。
阻塞因素
不幸的是,某些 JavaScript 操作將始終是同步的,包括:
以下示例展示了一個入侵者,它結合使用 CSS 動畫進行移動和 JavaScript 來揮動肢體。右側的圖像是基本的動畫 GIF。使用默認的 100,000 個 sessionStorage 操作點擊“寫入”按鈕:
[CodePen 示例鏈接 - 此處應插入CodePen的嵌入代碼]
在此操作期間,DOM 更新被阻止。在大多數瀏覽器中,入侵者會停止或卡頓。某些動畫 GIF 動畫將暫停。較慢的設備可能會顯示“腳本無響應”警告。
這是一個複雜的示例,但它演示了基本操作如何影響前端性能。
Web Workers
長時間運行進程的一種解決方案是 Web Workers。這些允許主瀏覽器應用程序啟動後台腳本並使用消息事件進行通信。例如:
// 单击按钮时引发事件 document.getElementById('clickme').addEventListener('click', handleClick); // 处理按钮单击事件 function handleClick(e) { // 获取要设置动画的元素 let sprite = document.getElementById('sprite'); if (!sprite) return; // 动画结束时删除“animate”类 sprite.addEventListener('animationend', () => { sprite.classList.remove('animate'); }); // 添加“animate”类 sprite.classList.add('animate'); }
Web Worker 腳本:
// main.js // 是否支持 Web Workers? if (!window.Worker) return; // 启动 Web Worker 脚本 let myWorker = new Worker('myworker.js'); // 从 myWorker 接收消息 myWorker.onmessage = e => { console.log('myworker sent:', e.data); } // 向 myWorker 发送消息 myWorker.postMessage('hello');
一個 worker 甚至可以生成其他 worker 來模擬複雜的線程式操作。但是,worker 的功能受到有意限制,並且 worker 不能直接訪問 DOM 或 localStorage(這樣做實際上會使 JavaScript 多線程化並破壞瀏覽器的穩定性)。因此,所有消息都作為字符串發送,這允許傳遞 JSON 編碼的對象,但不允許傳遞 DOM 節點。
Workers 可以訪問某些窗口屬性、WebSockets 和 IndexDB,但它們不會改進上面顯示的示例。在大多數情況下,worker 用於長時間運行的計算——例如光線追踪、圖像處理、比特幣挖掘等等。
(Node.js 提供子進程,它們類似於 Web Workers,但可以選擇運行其他語言編寫的可執行文件。)
硬件加速動畫
大多數現代瀏覽器不會阻塞在自己的圖層中運行的硬件加速 CSS 動畫。
默認情況下,上面的示例通過更改左外邊距來移動入侵者。此屬性和類似的屬性(例如 left 和 width)會導致瀏覽器在每個動畫步驟中重新流式傳輸和重新繪製整個文檔。
使用 transform 和/或 opacity 屬性時,動畫效率更高。這些實際上將元素放入單獨的合成圖層中,以便 GPU 可以單獨設置其動畫。
單擊“硬件加速”複選框,動畫將立即變得更流暢。現在嘗試另一個 sessionStorage 寫入;即使動畫 GIF 停止,入侵者也會繼續移動。請注意,肢體運動仍然會暫停,因為這是由 JavaScript 控制的。
內存存儲
在內存中更新對象的速度比使用寫入磁盤的存儲機制要快得多。在上面的示例中選擇“對象”存儲類型並點擊“寫入”。結果會有所不同,但它應該比等效的 sessionStorage 操作快大約 10 倍。
內存是易失性的:關閉標籤頁或導航離開會導致所有數據丟失。一個很好的折衷方案是使用內存對象來提高性能,然後在方便的時候永久存儲數據——例如在頁面卸載時:
// myworker.js // 接收消息时启动 onmessage = e => { console.log('myworker received:', e.data); // ...长时间运行的进程... // 发送回消息 postMessage('result'); };
遊戲或單頁應用程序可能需要更複雜的選擇。例如,保存數據的時間點:
Web 性能
Web 性能是一個熱門話題。開發人員受瀏覽器限制的約束較小,用戶期望獲得快速、類似操作系統的應用程序性能。
盡可能少地進行處理,DOM 將永遠不會被明顯阻塞。幸運的是,在無法避免長時間運行的任務的情況下,有一些選擇。
用戶和客戶端可能永遠不會注意到您的速度優化,但當應用程序變慢時,他們總是會抱怨!
關於 DOM 阻塞的常見問題解答 (FAQ)
DOM 阻塞是指瀏覽器無法渲染網頁,因為它正在等待腳本完成加載。這會大大降低網頁的加載速度,從而導致用戶體驗不佳。瀏覽器必須通過解析 HTML 標記來構建 DOM 樹。在此過程中,如果遇到腳本,則必須停止並執行它才能繼續。這是因為腳本可能會更改 DOM 樹結構,並且瀏覽器需要確保它擁有最新的視圖。
有幾種方法可以避免 DOM 阻塞。最有效的方法之一是使用異步加載腳本。這意味著腳本將在後台加載,而頁面其餘部分將繼續加載。另一種方法是推遲腳本,這意味著它們只會在 HTML 文檔完全解析後才執行。最後,您還可以將腳本移到 HTML 文檔的底部,以便它們是最後加載的內容。
同步腳本會阻塞 DOM 構造,直到它們完全加載並執行。這意味著如果腳本加載時間很長,它會延遲整個網頁。另一方面,異步腳本不會阻塞 DOM 構造。它們在後台加載,並且一旦準備好就可以執行,即使 DOM 尚未完全構建也是如此。
腳本標籤中的“defer”屬性用於指示腳本應在 HTML 文檔完全解析後執行。這意味著腳本不會阻塞 DOM 構造,從而加快網頁加載時間。但是,這也意味著腳本在 DOM 構造時可能尚未準備好,因此它只能用於不會更改 DOM 結構的腳本。
將腳本移到 HTML 文檔底部可確保它們是最後加載的內容。這意味著網頁的其餘部分可以在不等待腳本完成加載的情況下進行渲染。但是,此方法只能用於不會更改 DOM 結構的腳本,因為它們在 DOM 構造時可能尚未準備好。
腳本標籤中的“async”屬性用於指示腳本應異步加載。這意味著腳本將在後台加載,而網頁的其餘部分將繼續加載。腳本可以在準備好後立即執行,即使 DOM 尚未完全構建也是如此。這可以大大提高網頁加載時間,但它只能用於不會更改 DOM 結構的腳本。
DOM 阻塞會大大降低網頁的加載速度,這會對它的 SEO 排名產生負面影響。像 Google 這樣的搜索引擎將網頁加載速度視為排名因素之一。因此,避免 DOM 阻塞對於確保您的網頁在搜索引擎結果中排名靠前非常重要。
虛擬 DOM 是在 React 等現代 JavaScript 框架中使用的概念。它是實際 DOM 的副本,更改首先在虛擬 DOM 中進行,而不是在實際 DOM 中進行。一旦所有更改都完成,虛擬 DOM 將通過稱為協調的過程與實際 DOM 同步。這減少了對實際 DOM 的直接操作次數,從而加快了網頁加載時間。
您可以使用 Google 的 PageSpeed Insights 等工具來檢查您的網頁是否存在 DOM 阻塞問題。此工具會分析您的網頁並提供有關其性能的詳細報告,包括任何潛在的問題,例如 DOM 阻塞。
是的,CSS 也可能導致 DOM 阻塞。當瀏覽器遇到 CSS 文件時,它必須停止並加載它才能繼續渲染網頁。這是因為 CSS 文件可能包含會更改網頁外觀的樣式。為避免這種情況,您可以使用內聯關鍵 CSS 和推遲非關鍵 CSS 等方法。
以上是如何避免通過Localstorage和其他罪魁禍首阻止DOM的詳細內容。更多資訊請關注PHP中文網其他相關文章!