預取模組

Qwik 提供了各種策略來預先擷取模組。此頁面描述了 Qwik 預取功能的低階特性。

模組預取讓應用程式可以在使用者實際需要之前,於背景中開始下載必要的程式碼。理想的解決方案是僅預取與使用者互動高度相關且很有可能執行的最少程式碼量,同時避免任何「不會」被使用的 JavaScript。

Qwik 應用程式擅長下載和執行最少量的 JavaScript,從而優化資源使用和效能。透過瞭解個別元件的使用與否,Qwik 可以有效地決定應預取哪些套件。這種目標明確的方法可確保僅載入必要的程式碼。

請記住,可恢復性和激活之間的關鍵區別在於,可恢復性使 Qwik 應用程式能夠避免僅僅為了恢復事件監聽器、元件樹和應用程式狀態而執行 JavaScript。藉由從根本上分離元件的事件監聽器、渲染函式和狀態,與傳統方法相比,要預取的程式碼量要少得多。

收集已使用的符號

當 Qwik 渲染應用程式時,它能夠收集在渲染期間使用了哪些「符號」。符號包含元件的各個部分,這些部分由 優化器 提取,以分解應用程式。個別的事件監聽器、元件狀態和元件渲染器本身都是可以提取的不同符號的範例。

例如,考慮一個產品頁面,除了「加入購物車」按鈕之外,大部分都是靜態的。當按下按鈕時,使用者應該立即收到產品已加入購物車的回饋。在此範例中,Qwik 優化器將能夠理解使用者可以互動的唯一符號是「加入購物車」按鈕點擊事件監聽器。

對於我們的「加入購物車」範例,優化器僅針對點擊事件監聽器和加入購物車小工具的渲染器收集符號。不需要下載、激活和重新渲染與此無關的任何其他應用程式部分。這證明了 Qwik 能夠確定哪些互動是可能的,並且僅預取事件監聽器所需的程式碼。相比之下,傳統方法需要預取整個應用程式或路由(包括框架程式碼),才能添加點擊事件監聽器。

預取策略

預取策略是決定 Qwik 是否以及應該在背景中預取哪些 JavaScript 的邏輯。預設情況下,Qwik 將預取頁面上任何可見的監聽器。若要設定預取策略,請使用 renderToStream() 函式的 options 參數,該參數通常位於 src/entry.ssr.tsx 原始程式碼檔案中。提供最佳的預取策略是 Qwik 持續的承諾。

export default function (opts: RenderToStreamOptions) {
  return renderToStream(<Root />, {
    manifest,
    prefetchStrategy: {
      // custom prefetching config
    },
    ...opts,
  });
}

實作

瀏覽器提供了許多方法來實作預取策略。可以將 Qwik 設定為優先使用一種實作方式,每種方式都有其優缺點。根據此設定,產生的 HTML 內容將包含所選的預取實作。

export default function (opts: RenderToStreamOptions) {
  return renderToStream(<Root />, {
    manifest,
    prefetchStrategy: {
      implementation: {
        // custom prefetching implementation
      },
    },
    ...opts,
  });
}
選項說明
prefetchEvent使用包含應預取之網址的 detail 資料發送 qprefetch 事件。事件發送腳本將會嵌入到文件的 HTML 中。預設情況下,prefetchEvent 實作將設定為 always
linkInsert<link> 元素插入文件。使用 html-append 時,它會直接在 html 中渲染每個 <link>,附加在 body 的末尾。使用 js-append 選項時,它將改為插入一些 JavaScript,這些 JavaScript 會在執行階段建立元素並將其附加在 body 的末尾。
linkRel此選項用於定義 <link> 元素的 rel 屬性。當使用 linkInsert 選項時,默認值為 prefetch。其他選項包括 preloadmodulepreload
workerFetchInsert通過為每個模組呼叫 fetch() 來預取網址,目標是填充網路快取。

已發送的預取事件

推測模組提取 是首選的快取策略。此策略會偵聽 Qwik 架構發送的 qprefetch 事件。該事件包含背景執行緒應使用其預先填充瀏覽器 快取 的網址清單。

應將 Qwik 配置為使用 prefetchEvent 實作,這將發送 qprefetch 事件。預設情況下,prefetchEvent 實作將設定為 always。接下來,推測模組提取 將偵聽此事件並與其服務工作線程進行通訊,以保留請求/回應物件對,以便將其快取在長期記憶體中。

通過使用服務工作線程攔截來自瀏覽器的 fetch 請求,這種方法允許對快取進行精細控制,同時防止對相同資源的重複請求。

以下是手動發送事件的範例。這些事件是從 Qwik 本身發送的,不需要開發人員手動發送這些事件。此外,服務工作線程 將自動新增這些事件的偵聽器。

dispatchEvent(new CustomEvent("qprefetch", { detail: {
  bundles: [...]
}}));

使用帶有 rel 屬性的 <link> 元素是當今架構的常見方法,Qwik 可以通過配置 linkInsertlinkRel 選項來使用此方法。連結 rel 方法雖然有效,但在撰寫本文時,目前在所有裝置上都缺乏支援。此外,在開發過程中,假設它在任何地方都有效可能會產生誤導,因為行動裝置上的預取不容易被注意到。

例如,Safari 不支援 modulepreload。這一點很重要,因為行動裝置可能從模組預載中獲益最多。同樣,Firefox 在 https 上時也不支援連結 rel prefetch

預取是一項旨在提高訪客體驗速度的功能。但是,其有效性可能會因使用的瀏覽器和 CDN/伺服器的組合而異,這凸顯了優化設定以確保最佳效能的重要性。

- Rel=prefetch 與有效 HTTP/2 優先順序的重要性

此外,可能會對同一資源發出多個請求。例如,假設我們要預取 module-a.js,並且在下載過程中,無論需要多長時間,使用者都會與應用程式互動。然後,應用程式決定實際請求並執行 module-a.js。在撰寫本文時,瀏覽器通常會發出第二個請求,使情況變得更糟。

Web 工作線程提取

workerFetchInsert 指示 Qwik 使用 Web Worker 來 fetch() JavaScript 文件,目標是使用模塊預先載入瀏覽器快取。通過使用 Web Worker,提取和快取邏輯位於另一個線程上。提取響應也將具有 immutable 或長的緩存控制標頭,因此瀏覽器不會發出第二次網絡請求。

此設置的缺點是提取的響應被丟棄,並且僅在瀏覽器級別希望文件被緩存。

預取常見問題

問題使用者事件的延遲加載是否很慢,因為使用者必須等待代碼下載?

是的,這會造成明顯的延遲,尤其是在網路速度慢的情況下。這就是為什麼代碼預取是 Qwik 應用程式的重要組成部分。

預取代碼可確保在導航到頁面時立即提取運行應用程式所需的所有代碼。這樣,當使用者執行操作時,該操作的代碼來自預取緩存而不是網路。結果是代碼執行是即時的。

問題代碼預取是否會導致與現有框架相同的行為,即急切地下載和執行所有代碼?

不,原因如下:

  • 現有框架必須下載並執行所有代碼(水合作用),然後應用程式才能進行交互。通常,代碼的下載時間成本低於代碼的執行時間成本。
  • Qwik 代碼預取僅下載但不執行代碼。因此,即使 Qwik 預取與現有框架相同數量的代碼,結果也會節省大量的時間成本。
  • Qwik 僅預取當前頁面所需的代碼。 Qwik 避免下載與靜態組件相關聯的代碼。在需要預取更多代碼的情況下,Qwik 仍然只會達到現有框架認為的最佳情況。在大多數情況下,與現有框架相比,Qwik 預取的代碼量很少。
  • 代碼的預取可以在主線程以外的其他線程上進行。許多瀏覽器甚至可以在主線程之外預解析代碼的 AST。
  • 如果使用者交互發生在預取完成之前,瀏覽器將自動優先處理交互區塊,而不是剩餘的預取區塊。
  • Qwik 可以將應用程式分解成許多小區塊,並且可以按照使用者與它們交互的概率順序下載這些區塊。現有框架難以將應用程式分解成小區塊,並且沒有簡單的方法可以優先處理區塊下載順序,因為水合作用需要應用程式的單個“主”入口點。

問題誰負責知道要預取哪些代碼?

Qwik 可以自動生成預取指令作為 SSR 渲染的一部分。通過執行應用程式,Qwik 可以實時了解哪些組件可見,使用者可以觸發哪些事件以及需要下載哪些代碼。結果是預取是此頁面的理想文件集。除了將預取策略添加到 renderToStream() 之外,開發人員無需採取任何行動。

貢獻者

感謝所有為改進本文檔做出貢獻的貢獻者!

  • adamdbradley
  • RATIU5
  • manucorporat
  • literalpie
  • saikatdas0790
  • the-r3aper7
  • mhevery
  • mrhoodz
  • thejackshelton
  • maiieul
  • jemsco