透過串流方式立即回應

傑夫波斯尼克
Jeff Posnick

只要是使用服務工作站的使用者,都能告知您他們目前是以非同步方式執行。這類 API 完全仰賴事件型介面 (例如 FetchEvent),並在非同步作業完成時使用承諾發出信號。

非同步程度也同樣重要,但在服務工作工作站擷取事件處理常式提供的回應時,開發人員較不容易察覺。串流回應是這裡的黃金標準:這類回應可讓提出原始要求的頁面在第一個資料區塊可用時立即開始使用回應,並且可能使用針對串流而最佳化的剖析器,以便逐步顯示內容。

編寫自己的 fetch 事件處理常式時,常見的做法是將透過 fetch()caches.match() 取得的 Response (或 Response 的承諾產品) 傳遞,然後一天呼叫該處理常式。respondWith()好消息是,這兩種方法建立的 Response 都可以串流!但好消息是,至少目前「手動」Response 無法串流處理。這時 Streams API 輸入圖片的位置。

訊息串、

串流是一種可逐步建立和控制的資料來源,並且提供一個介面,用於讀取或寫入非同步資料區塊,但只能在記憶體中隨時可用的子集。目前我們想瞭解 ReadableStream,可用以建構傳遞至 fetchEvent.respondWith()Response 物件:

self.addEventListener('fetch', event => {
    var stream = new ReadableStream({
    start(controller) {
        if (/* there's more data */) {
        controller.enqueue(/* your data here */);
        } else {
        controller.close();
        }
    });
    });

    var response = new Response(stream, {
    headers: {'content-type': /* your content-type here */}
    });

    event.respondWith(response);
});

呼叫 event.respondWith() 後,要求觸發 fetch 事件的網頁會立即收到串流回應,而且只要服務工作站持續 enqueue() 還有資料,就能繼續從該串流讀取資料。從 Service Worker 傳送至頁面的回應完全非同步,而且我們可以完全控制串流!

實際用途

您可能已經注意到,上一個範例有一些預留位置 /* your data here */ 註解,對於實際的實作細節沒有幫助。 實際案例看起來會是什麼樣子?

Jake Archibald (其實意想不到!) 有絕佳範例,說明如何使用串流,將多個快取 HTML 程式碼片段的 HTML 回應拼接,以及透過 fetch() 串流的「即時」資料。在本例中,其網誌內容

正如 Jake 所述,使用串流回應的好處是瀏覽器可以在進行串流時剖析並算繪 HTML,包括從快取快速載入的初始位元,不必等待整個網誌內容擷取完成。這可充分運用瀏覽器的漸進式 HTML 轉譯功能。此方法也能逐步轉譯的其他資源,例如某些圖片和影片格式,也能受益於這種做法。

訊息串、還是應用程式殼層?

目前使用 Service Worker 支援網頁應用程式的最佳做法的最佳做法,著重於以 App Shell + 動態內容模型為主。此方法仰賴積極快取網頁應用程式的「殼層」(即顯示結構和版面配置所需的最低 HTML、JavaScript 和 CSS),然後透過用戶端要求載入各特定網頁所需的動態內容。

串流可提供另一個 App Shell 模型的替代方案,在這個模型中,當使用者前往新頁面時,瀏覽器會以串流方式將完整的 HTML 回應串流至瀏覽器中。串流回應可使用快取資源,因此即使離線時也能快速提供 HTML 的初始區塊,但最終看起來會像是傳統的伺服器轉譯回應主體。舉例來說,如果您的網頁應用程式採用內容管理系統,而伺服器拼接部分部分範本來算繪 HTML,則該模型會直接轉譯為使用串流回應,範本邏輯會複製到 Service Worker,而非伺服器。請參閱以下影片示範,針對該使用情境,串流回應提供的速度優勢可能相當引人注目:

串流整個 HTML 回應的重要優點之一,就是說明為何這是影片最快的替代選項。也就是說,在初始瀏覽要求期間轉譯的 HTML 可充分運用瀏覽器的串流 HTML 剖析器。頁面載入後插入文件的 HTML 區塊 (如 App Shell 模型很常見),因此無法利用這項最佳化功能。

因此,如果您正在服務 worker 實作階段,應該採用哪個模型:漸進式轉譯的串流回應,或與用戶端要求搭配用戶端要求的輕量殼層使用?答案並不令人意外,這取決於以下因素:您是否需要依賴 CMS 和部分範本的實作項目 (優點:串流);是否預期單一、大型 HTML 酬載,是否可透過漸進式轉譯 (優點:串流) 而受益;或者,您是否最適合採用單頁應用程式模式:

服務工作人員提供的串流回應還很早,很期待看到不同的模型成熟,尤其是將更多開發的工具化為自動執行常見用途。

深入探索串流內容

如果您打算建構可讀取的串流,那麼隨意呼叫 controller.enqueue() 可能不夠有效或效率。Jake 詳細說明瞭如何搭配使用 start()pull()cancel() 方法,並根據您的用途建立專屬的資料串流。

如需更多詳細資料,請參閱串流規格

相容性

Chrome 52 已新增使用 ReadableStream 做為來源,因此支援在服務工作站內建構 Response 物件。

Firefox 的 Service Worker 實作尚未支援由 ReadableStream 支援的回應,但你可以追蹤串流 API 支援的相關追蹤錯誤

您可以透過 Microsoft 的平台狀態頁面,追蹤 Edge 中尚未加上前置字串的 Streams API 支援進度,以及整體服務工作站支援