在前端開發中,JavaScript 的阻塞載入會嚴重影響網頁的效能和使用者體驗。當瀏覽器解析 HTML 遇到 <script>
標籤時,會停止解析 HTML,優先下載並執行 JavaScript 程式碼,然後再繼續解析 HTML。如果 JavaScript 程式碼執行時間過長,就會導致頁面渲染延遲,出現“白屏”現象。
為了避免 JavaScript 阻塞載入,可以採用以下幾種方法:
1. 使用 <script async>
屬性:
async
屬性告訴瀏覽器在下載 JavaScript 檔案的同時繼續解析 HTML。JavaScript 檔案下載完成後會立即執行,不會阻塞 HTML 解析。多個帶有 async
屬性的指令碼的執行順序是不確定的,哪個先下載完哪個先執行。
<script async src="script1.js"></script>
<script async src="script2.js"></script>
2. 使用 <script defer>
屬性:
defer
屬性也告訴瀏覽器在下載 JavaScript 檔案的同時繼續解析 HTML。與 async
不同的是,帶有 defer
屬性的指令碼會在 HTML 解析完成後,DOMContentLoaded 事件觸發之前執行。多個帶有 defer
屬性的指令碼會按照它們在 HTML 中出現的順序執行。
<script defer src="script1.js"></script>
<script defer src="script2.js"></script>
3. 將 <script>
標籤放在 <body>
的底部:
將 <script>
標籤放在 <body>
的底部,可以確保在執行 JavaScript 程式碼之前,HTML 的主體內容已經解析完成並渲染出來。這樣可以避免 JavaScript 阻塞 HTML 解析,提高使用者體驗。
<body>
<!-- HTML content -->
<script src="script.js"></script>
</body>
4. 使用動態指令碼插入:
透過 JavaScript 動態建立 <script>
元素並將其新增到文件中,可以實現非同步載入 JavaScript 檔案。
function loadScript(src) {
const script = document.createElement('script');
script.src = src;
script.async = true; // 可選,新增 async 屬性
document.head.appendChild(script);
}
loadScript('script1.js');
loadScript('script2.js');
5. 使用 import() 動態匯入 (ES Modules):
對於使用 ES Modules 的專案,可以使用 import()
動態匯入模組。這允許按需載入 JavaScript 模組,並且是非同步的。
import('./myModule.js').then(module => {
// 使用匯入的模組
module.doSomething();
});
選擇哪種方法?
- 如果指令碼之間沒有依賴關係,並且不需要訪問 DOM,可以使用
async
。 - 如果指令碼之間有依賴關係,或者需要訪問 DOM,並且需要在 DOMContentLoaded 事件觸發之前執行,可以使用
defer
。 - 將
<script>
標籤放在<body>
底部是一種簡單且有效的方法,適用於小型專案或對效能要求不高的場景。 - 動態指令碼插入和
import()
動態匯入提供了更大的靈活性,適用於需要按需載入 JavaScript 模組的場景。
透過以上方法,可以有效避免 JavaScript 阻塞載入,提高網頁的效能和使用者體驗。 選擇哪種方法取決於你的具體需求和專案規模。 對於現代瀏覽器,async
和 defer
以及 import()
都是很好的選擇。