預先載入模組

Sérgio Gomes

發布日期:2024 年 11 月 23 日

以模組為基礎的開發作業在快取可用性方面具有實質優勢,可協助您減少傳送給使用者的位元組數。程式碼的精細程度越高,您就能越優先處理應用程式中的關鍵程式碼,這也有助於載入流程。

不過,模組依附元件會導致載入問題,因為瀏覽器必須等待模組載入,才能找出其依附元件。解決這個問題的方法之一,就是預先載入依附元件,讓瀏覽器提前瞭解所有檔案,並保持連線忙碌狀態。

<link rel="preload"> 是一種方式,可在瀏覽器需要資源之前,預先宣告要求資源。

<head>
  <link rel="preload" as="style" href="critical-styles.css">
  <link rel="preload" as="font" crossorigin type="font/woff2" href="myfont.woff2">
</head>

這項功能特別適合用於字型等資源,因為這類資源通常會隱藏在 CSS 檔案中,有時甚至會位於多個層級。在這種情況下,瀏覽器必須等待多次來回傳輸,才能發現需要擷取大型字型檔案,而這段時間本來可以用來開始下載,並充分利用完整的連線頻寬。

<link rel="preload"> 及其等同的 HTTP 標頭提供簡單的宣告式方式,讓瀏覽器立即瞭解目前導覽所需的重要檔案。瀏覽器看到預先載入時,就會開始以高優先順序下載資源,這樣在實際需要時,資源就會已擷取或部分擷取。但不適用於模組。

這就是棘手之處。資源有幾種憑證模式,為了取得快取命中,這些模式必須相符,否則您最終會擷取兩次資源。不用說,重複擷取是不好的做法,因為這會浪費使用者的頻寬,並讓他們無故等待更久的時間。

針對 <script><link> 標記,您可以使用 crossorigin 屬性設定憑證模式。不過,實際上,沒有 crossorigin 屬性的 <script type="module"> 會指出 omit 的憑證模式,而 <link rel="preload"> 並沒有這類模式。也就是說,您必須將 <script><link> 中的 crossorigin 屬性都變更為其他值,如果您嘗試預先載入的項目是其他模組的依附元件,可能就沒有簡單的方法可以執行這項操作。

此外,擷取檔案只是實際執行程式碼的第一步。首先,瀏覽器必須剖析及編譯該檔案。理想情況下,這項作業也應提前完成,以便在需要模組時,程式碼已準備就緒。不過,V8 (Chrome 的 JavaScript 引擎) 會以與其他 JavaScript 不同的方式剖析及編譯模組。<link rel="preload"> 不會提供任何方式,指出要載入的檔案是模組,因此瀏覽器只能載入檔案並將其放入快取。使用 <script type="module"> 標記載入指令碼 (或由其他模組載入) 後,瀏覽器會剖析並編譯程式碼,做為 JavaScript 模組。

簡單來說,答案是肯定的。透過為預先載入模組提供特定的 link 類型,我們可以編寫簡單的 HTML,而不必擔心所使用的憑證模式為何。預設值即可正常運作。

<head>
  <link rel="modulepreload" href="super-critical-stuff.mjs">
</head>
[...]
<script type="module" src="super-critical-stuff.mjs">

由於瀏覽器現在知道您正在預先載入的內容是模組,因此可以聰明地在擷取完成後立即剖析及編譯模組,而不會等到嘗試執行時才執行。

瀏覽器支援

  • Chrome:66。
  • Edge:≤79。
  • Firefox:115。
  • Safari:17。

資料來源

但模組的依附元件呢?

你問得好!這篇文章確實沒有涵蓋到遞迴:

<link rel="modulepreload"> 規格其實允許您選擇性載入所要求的模組,以及其所有依附元件樹狀圖。瀏覽器不一定要這樣做,但可以這麼做。

因此,如果您需要完整的依附元件樹狀結構才能執行應用程式,那麼,哪一種跨瀏覽器解決方案最適合預先載入模組及其依附元件樹狀結構?

選擇遞迴預先載入依附元件的瀏覽器應具備強大的模組去重功能,因此一般最佳做法是宣告模組和其依附元件的平面清單,並信任瀏覽器不會擷取相同的模組兩次。

<head>
  <!-- dog.js imports dog-head.js, which in turn imports
       dog-head-mouth.js, which imports dog-head-mouth-tongue.js. -->
  <link rel="modulepreload" href="dog-head-mouth-tongue.mjs">
  <link rel="modulepreload" href="dog-head-mouth.mjs">
  <link rel="modulepreload" href="dog-head.mjs">
  <link rel="modulepreload" href="dog.mjs">
</head>

預先載入模組是否有助於提升效能?

預先載入可協助瀏覽器盡量使用頻寬,因為瀏覽器會知道需要擷取哪些內容,因此不會在長時間的往返過程中閒置。如果您正在嘗試使用模組,但因深層依附元件樹狀圖而發生效能問題,建立預先載入的平面清單肯定有助於解決問題。

不過,我們仍在努力改善模組效能,因此請務必使用「開發人員工具」仔細查看應用程式發生的情況,並考慮將應用程式分成多個區塊。不過,Chrome 中仍有許多正在進行的模組工作,因此我們正努力讓 bundler 能休息一下!