PWA 設定聖誕老人追蹤器

瀏覽網站

摘要

Santa Tracker 已迅速升級為 2016 年節慶季節期間的離線漸進式網頁應用程式,這部分是我們現有的場景設計的一部分。

成果

  • 聖誕老人是漸進式網頁應用程式 (PWA),支援新增到主畫面 (ATHS) 和離線觀看
  • 在符合資格的課程中,10% 會點選 ATHS 圖示
  • 75% 的使用者原生支援自訂元素和 shadow DOM,兩者為網頁元件的兩大核心
  • Lighthouse 分數:81 分
  • 透過 Service Worker API 離線,使用延遲載入功能只快取造訪的場景,並在新版本中自行升級

背景

聖誕老人追蹤器是 Google 的節慶傳統。 每年 12 月都有遊戲和教育體驗,讓你歡慶佳節。雖然聖誕老人正休息片刻,但小精靈決定以開放原始碼的形式發布聖誕老人追蹤器,且開放網頁版Android 版。

網路上的聖誕老人追蹤器是一個大型互動式網站,提供許多獨特的「場景」(使用 Polymer 編寫),可支援大多數新型瀏覽器。透過功能偵測,判斷使用者瀏覽器是否「現代化」:聖誕老人需要 SetWeb Performance API 等。

2016 年,我們升級了聖誕老人追蹤器後的引擎,因此大多數場景都能支援離線體驗。 其中不含 YouTube 影片支持的場景,或是處理聖誕老人目前位置的場景 (當然,僅與北極有直接關係)。📶☃️

Android 裝置上的聖誕老人追蹤器
Android 裝置上的聖誕老人追蹤器

挑戰

聖誕老人融入了回應式設計,在手機、平板電腦和電腦上都能順利運作。網站提供多種優質多媒體內容,包括風格獨具的視覺畫面和節慶主題音效。然而,定期建立的聖誕老人追蹤器有幾百 MB! 原因如下:

  • 聖誕老人支援超過 35 種語言,因此必須複製許多素材資源。
  • 每個平台支援的媒體平台各有不同 (例如 mp3 和 ogg)。
  • 多媒體檔案有時會以不同的大小和解析度提供。

聖誕老人的小精靈也在 12 月全力工作,整個月都會持續發布新的重大更新。也就是說,使用者瀏覽器已快取的素材資源可能會在使用者再次造訪時重新整理。

這些挑戰包括:

  • 適用於不同場景的大型多媒體資源
  • 這一個月內發布的變更

...導致不適用單純的離線策略。

使用 Polymer 打造的聖誕老人

建議先回頭介紹聖誕老人的整體設計,再深入探討我們如何將其升級為離線 PWA。

Santa 是單頁應用程式,最初是以 Polymer 0.5 編寫,現在升級為 Polymer 1.7。「聖誕老人」是由一組共用程式碼所組成, 例如路由器、共用的導航資產等,此外還有許多獨特的「場景」。

友善載入器

每個場景都是自己的網頁元件,可透過不同網址 (/village.html/codelab.html/boatload.html) 存取。當使用者開啟場景時,我們會預先載入所有必要的 HTML 和資產 (圖片、音訊、css、js),這些項目都在聖誕老人追蹤器存放區的 /scenes/[[sceneName]] 底下。更新完畢後,使用者就會透過友善的預載器掌握進度。

這種方法也意味著我們不必為使用者看不到的場景 (因為大量資料) 載入不必要的素材資源。這也意味著我們需要保留每個場景所需要之所有資產的內部「快取資訊清單」。快取資訊清單是 JSON 檔案,用於儲存檔案名稱與內容 MD5 雜湊的對應關係。

載入所用資源

這個模型可以節省頻寬,而且只會提供使用者造訪情境所需的資源,而不是一次預先載入整個網站。聖誕老人追蹤器利用 Polymer 在執行階段 (而非在載入時) 升級自訂元素的功能。請看以下程式碼片段:

<lazy-pages id="lazypages" selected-item="&#123;{selectedScene}}" ... >
    <dorf-scene id="village" route="village" icon="1f384" permanent
        mode$="[[mode]]"
        path$="scenes/dorf/dorf-scene_[[language]].html"
        class="santa-scene" allow-page-scrolling></dorf-scene>

    <boatload-scene route="boatload" icon="26f5"
        path$="scenes/boatload/boatload-scene_[[language]].html"
        loading-bg-color="#8fd7f7"
        loading-src="scenes/boatload/img/loading.svg"
        logo="scenes/boatload/img/logo.svg"
        class="santa-scene"></boatload-scene>

聖誕老人追蹤器會按照下列步驟載入場景 (例如boatload-scene:

  1. 所有場景元素 (包括 <boatload-scene>) 最初不明,都會視為 HTMLUnknownElement 具有一些額外屬性。
  2. 變更所選場景時,系統會通知 <lazy-pages> 元素。
  3. <lazy-pages> 元素會解析場景元素和 path 屬性,載入 HTML 匯入 scenes/boatload/boatload-scene_en.html。其中包含 Polymer 元素和其相依元素。
  4. 顯示易於使用的預載器。
  5. 載入並執行 HTML 匯入作業後,<boatload-scene> 會以透明化的方式升級至實際的 Polymer 元素,充滿節慶歡呼聲。🎄🎉

這個方法也存在著挑戰。舉例來說,我們不希望納入重複的網頁元件。如果兩個場景使用共通元素 (例如paper-button,我們會將其移除,做為建構程序的一部分,改為將其納入聖誕老人的共用程式碼中。

離線設計

多虧了 Polymer 和 lazy-pages,聖誕老人追蹤器已在場景中清楚地分段,每個場景都有自己的目錄。我們設計了聖誕老人追蹤器的 Service Worker,這是啟用離線功能 (可在使用者瀏覽器中運作) 的核心元件,以便瞭解共用程式碼與「情境」的差異。

Service Worker 背後的理論為何?當支援瀏覽器的使用者載入您的網站時,前端 HTML 可以要求安裝服務工作站。對於聖誕老人追蹤器,服務工作處理程序就位於 /sw.js。這會觸發 install 事件,並預先快取聖誕老人的所有共用程式碼,因此不必在執行階段擷取任何內容。

SW 流程圖

Service Worker 安裝完成後,就能攔截所有 HTTP 要求。對聖誕老人追蹤器而言,簡化的決策流程如下:

  1. 這項要求是否已快取?
    • 太好了!傳回快取的回應。
  2. 要求是否與場景目錄 (例如「 scene/Craftload/Craftload-scene_en.html」) 相符?
    • 執行網路要求,並將結果儲存在快取中,再將結果傳回使用者。
  3. 否則,請執行一般網路要求。

我們的流程和 install 事件允許載入聖誕老人追蹤器,即使使用者離線也不受影響。但系統只會提供使用者先前載入的場景。 適合重播及超越最高分的玩家。

Keen 觀察器可能註明,我們的快取策略不允許內容的「變更」。檔案在使用者的瀏覽器中快取後,永遠不會改變。 稍後會再詳細討論。

我們會實際執行

如先前所述,聖誕老人的小精靈非常努力在 12 月工作,他們經常必須在整個月內發布新版本。「聖誕老人追蹤器」發行後,它會有一個專屬標籤,例如v20161204112055,發布內容的時間戳記 (2016 年 12 月 4 日 11:20:55)。

針對這個加上標籤的版本,我們會為每個檔案產生 MD5 雜湊,並儲存在我們的「快取資訊清單」中。在新型固態磁碟上,這只會增加數秒的建構程序。

每個版本都會部署至 Google 靜態快取伺服器中的專屬路徑。也就是說,系統絕不會移除較舊的版本。 這表示在新版本推出後,所有資產都會有不同的網址 (即使資產並未變更),且除非進行額外作業,否則瀏覽器或服務工作站快取的所有資訊都無法使用。

我們也部署了新版的「實際工作環境」資源 (Santa 的索引 HTML 和 Service Worker),網址為 https://santatracker.google.com/。這項操作會覆寫舊版本。

靜態圖表

每當載入聖誕老人追蹤器時,瀏覽器都會檢查並擷取已更新的 Service Worker (如果有的話)。 在本例中,每個版本都會產生位元組不同的程式碼。瀏覽器會將此視為升級,並執行新的 install 事件。

此時,使用者的瀏覽器會查看新的「快取資訊清單」。 系統會比對使用者現有的快取,如果資產使用不同的 MD5 雜湊,我們就會從快取中刪除,並要求瀏覽器重新擷取。不過,在多數情況下,快取內容大致相同,或只有細微差異。

快取圖表

在聖誕老人追蹤器中升級 Service Worker 後,使用者的瀏覽器會立即重新載入。

離線瀏覽體驗

當然,為了支援離線體驗,我們也必須變更使用者介面,並讓對於未預期網站可離線運作的使用者更容易瞭解。

離線瀏覽時,系統會顯示小型橫幅。 所有未快取的場景都會「凍結」且無法點擊。這樣一來,使用者就無法存取無法使用的內容。

離線

聖誕老人追蹤器會定期向聖誕老人 API 發出要求。 如果這些要求失敗或逾時,我們會假設使用者處於離線狀態。我們會使用這個 API,而非瀏覽器內建的 navigator.onLine 屬性:這個 API 只會告訴我們使用者是否已在線上。(也稱為 Lie-Fi)。

跨國連線

雖然我們大部分使用者都是英文 (後來日文、葡萄牙文、西班牙文和法文),不過她以超過 35 種不同的語言打造與發布服務,

當使用者載入聖誕老人追蹤器時,我們會使用瀏覽器語言和其他提示來選擇要提供的語言。大多數使用者絕不會覆寫這個語言。 然而,如果使用者透過挑選器選擇新語言,我們會將此視為有可用的升級,如同上述案例,在新版聖誕老人追蹤器推出時一樣。

語言

換句話說,目前用於服務工作站的聖誕老人追蹤器版本實際上是 (build,language) 的組合。

新增至主畫面

由於聖誕老人可離線運作並提供 Service Worker,因此符合資格的使用者會在主畫面中收到安裝提示。在 2016 年,約有 10% 的合格載入量來自主畫面圖示。

結語

我們成功將聖誕老人追蹤器快速轉換成離線的 PWA,這與現有的場景設計一致,而且透過現有的 Polymer 和網頁元件輕鬆完成。還能運用我們的建構系統執行有效率的升級作業,只讓已變更的資產失效。

雖然 Santa 主要是自訂的解決方案,但您可以在 Polymer 專案的 App Toolbox 中找到它的許多原則。建議您從頭建構新的 PWA;如果正在尋找跨架構的方法,不妨考慮使用Workbox 程式庫