
上次更新日期:2019-04-30
網頁應用程式的漸進式網頁應用程式為何?
漸進式網路應用程式可在電腦版和行動版平台上,提供類似應用程式的專屬安裝體驗。他們所打造的網路應用程式不僅快速可靠,最重要的是,它們是可以在任何瀏覽器中運作的網路應用程式。如果您正在建構網路應用程式,表示您已著手打造漸進式網路應用程式。
快速與高品質
每項網頁體驗都必須快速,而對於漸進式網路應用程式更是如此。「快速」是指在螢幕上顯示有意義的內容,以及提供互動體驗所需的時間。
且必須穩定可靠。對壓力而言,實在力氣之外,也難以感到極具壓力。請仔細思考一下:原生應用程式的首次載入是令人困擾的。雖然它有應用程式商店和大螢幕下載,但是當您到達某個安裝應用程式的位置後,所有應用程式啟動的前期成本將會有所改變,而且所有開始的延遲時間都沒有變化。每次啟動應用程式的速度都和最後一個應用程式一樣快,沒有任何差異。漸進式網頁應用程式必須為使用者提供穩定可靠的效能。
可安裝
漸進式網路應用程式可在瀏覽器分頁中執行,但也可安裝。將網站加入書籤會新增捷徑,但已安裝的漸進式網路應用程式 (PWA) 的外觀和行為和所有其他已安裝的應用程式一樣。和其他應用程式啟動一樣。您可以控制啟動功能,包括自訂啟動畫面、圖示等等。應用程式會以應用程式的形式,在沒有網址列或其他瀏覽器 UI 的應用程式視窗中運作。就像所有其他已安裝的應用程式一樣,它也是工作切換器中的頂層應用程式。
請記住,可安裝的 PWA 相當快速又可靠。安裝 PWA 時,使用者會預期應用程式能夠正常運作,不受網路連線類型影響。此標識是每個安裝的應用程式都必須符合這項基準。
行動裝置和行動裝置
PWA 採用回應式設計技術,在行動裝置和桌機之間皆可使用,而且跨平台使用單一程式碼庫。如果您正在考慮編寫一個原生應用程式,請看看 PWA 能帶來哪些優勢。
建構項目
在本程式碼研究室中,您將使用 PWA 技術建構天氣網頁應用程式。您的應用程式將會:
- 使用回應式設計,在桌機或行動裝置上皆可使用。
- 透過服務工作站快速快取需要執行的應用程式資源 (HTML、CSS、JavaScript、圖片),並在執行階段快取快取資料以改善效能。
- 可供安裝,使用網路應用程式資訊清單和
beforeinstallprompt事件通知使用者其可安裝。

您將會瞭解的內容
- 如何建立及新增網頁應用程式資訊清單
- 如何提供簡易的離線體驗
- 如何提供完整的離線體驗
- 如何安裝您的應用程式
本程式碼研究室著重於漸進式網路應用程式。內含不相關的概念和程式碼區塊會重疊在外,可供您直接複製並貼上。
軟硬體需求
- 最新版本的 Chrome (74 以上版本)。PWA 支援所有瀏覽器,但我們會使用 Chrome 開發人員工具的其中一些功能,進一步瞭解瀏覽器層級的功能,並用來測試安裝體驗。
- 瞭解 HTML、CSS、JavaScript 和 Chrome DevTools。
取得 Dark Sky API 的金鑰
我們的天氣資料來自 Dark Sky API。如要使用,您必須要求 API 金鑰。非商業用途專案易於使用。
確認您的 API 金鑰運作正常
如要測試 API 金鑰是否正常運作,請對 DarkSky API 發出 HTTP 要求。請將下方網址改成您 API 金鑰的「DARKSKY_API_KEY」。如果一切正常,您就會看到紐約市的最新天氣預報資訊。
https://api.darksky.net/forecast/DARKSKY_API_KEY/40.7720232,-73.9732319
取得程式碼
我們已將本專案所需的資料都放到 Git 存放區中。如要開始使用,請先擷取程式碼,然後在您偏好的開發環境中開啟此程式碼。這個程式碼研究室建議使用 Glitch。
強烈建議:使用 Glitch 匯入存放區
使用 Glitch 是完成此程式碼研究室的建議方法。
- 開啟新的瀏覽器分頁,然後前往 https://glitch.com。
- 如果您沒有帳戶,請先申請。
- 按一下 [新增專案],然後點選 [從 Git Repo 複製]。
- 複製 https://github.com/googlecodelabs/your-first-pwapp.git 並按一下 [確定]。
- 存放區載入完成後,請編輯
.env檔案,並使用 DarkSky API 金鑰進行更新。 - 按一下 [顯示] 按鈕,然後選擇 [在新視窗中開啟],即可查看 PWA 實際運作的情形。
替代方案:下載程式碼 &方塊 (可於本機運作)
如果您想下載程式碼並在本機執行,您必須安裝最新版本的 Node.js,並使用程式碼編輯器設定,並且可以開始使用。
- 解壓縮下載的 ZIP 檔案。
- 執行
npm install,以安裝執行伺服器所需的依附元件。 - 編輯
server.js並設定 DarkSky API 金鑰。 - 執行
node server.js以在通訊埠 8000 啟動伺服器。 - 開啟瀏覽器分頁以http://localhost:8000
我們的起點是什麼?
我們的起點是基本的程式碼應用程式,專為程式碼研究室設計。經過簡化,讓這個程式碼研究室的概念變得精簡,而且幾乎沒有錯誤處理。如果您選擇在正式版應用程式中重複使用任何程式碼,請務必處理所有錯誤並完整測試所有程式碼。
你可以試試以下功能...
- 使用右下角的藍色 + 按鈕新增城市。
- 使用右上角的 [重新整理] 按鈕重新整理資料。
- 使用每一張城市資訊卡右上方的 x 刪除城市。
- 使用 Chrome 開發人員工具的切換裝置工具列,即可瞭解這項工具在電腦和行動裝置上的運作方式。
- 使用 Chrome 開發人員工具的「網路」面板,瞭解離線時會發生什麼情況。
- 使用 Chrome 開發人員工具的「網路」面板,查看網路節流至 Slow 3G 時會發生什麼情況。
- 變更
server.js中的FORECAST_DELAY值,讓預測伺服器增加延遲
透過 Lighthouse 稽核
Lighthouse 是一款簡單易用的工具,可協助您改善網站和網頁的品質。Lighthouse 會針對效能、無障礙功能、漸進式網頁應用程式和其他項目執行稽核。每項稽核都有一份參考說明文件,說明稽核的重要性,以及如何修正問題。

我們會使用 Lighthouse 稽核我們的天氣應用程式,並驗證我們所做的調整。
一起執行 Lighthouse
- 在新分頁中開啟專案。
- 開啟 Chrome 開發人員工具並切換至「稽核」面板。讓所有稽核類型保持啟用狀態。
- 按一下 [執行稽核]。經過一段時間後,Lighthouse 會提供報告報告。
漸進式網頁應用程式稽核
我們會將重點放在漸進式網頁應用程式稽核的結果。

還有許多紅色重點值得注意:
- 減少失敗:目前的網頁無法在離線狀態下傳回 200 錯誤。
- 減少失敗:
start_url在離線時不會傳回 200 代碼。 - 單純失敗:未註冊控制網頁和
start_url.的服務業者 - 減少失敗:網頁應用程式資訊清單不符合安裝規定。
- 減少失敗:並未針對自訂啟動畫面進行設定。
- 單純失敗:不設定網址列主題的顏色。
讓我們立刻開始修正部分問題!
本節最後,我們的天氣應用程式通過下列稽核:
- 網頁應用程式資訊清單不符合安裝規定。
- 未設定自訂啟動畫面。
- 未設定網址列主題顏色。
建立網頁應用程式資訊清單
網頁應用程式資訊清單是一個簡單的 JSON 檔案,可讓開發人員控管應用程式向使用者顯示的方式。
使用網路應用程式資訊清單,您的網頁應用程式可以:
- 指定要在獨立視窗 (
display) 中開啟應用程式的瀏覽器。 - 定義首次啟動應用程式 (
start_url) 時要開啟的網頁。 - 定義應用程式在座架或應用程式啟動器中的外觀 (
short_name;icons)。 - 建立啟動畫面 (
name、icons、colors)。 - 請瀏覽器以橫向或縱向模式開啟視窗 (
orientation)。 - 還有更多功能。
在專案中建立名為 public/manifest.json 的檔案。複製並貼上以下內容:
public/manifest.json
{
"name": "Weather",
"short_name": "Weather",
"icons": [{
"src": "/images/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png"
}, {
"src": "/images/icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png"
}, {
"src": "/images/icons/icon-152x152.png",
"sizes": "152x152",
"type": "image/png"
}, {
"src": "/images/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}, {
"src": "/images/icons/icon-256x256.png",
"sizes": "256x256",
"type": "image/png"
}, {
"src": "/images/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}],
"start_url": "/index.html",
"display": "standalone",
"background_color": "#3E4EB8",
"theme_color": "#2F3BA2"
}
資訊清單支援各種螢幕大小的圖示。在這個程式碼研究室中,我們新增了幾項 iOS 整合功能。
在網頁應用程式資訊清單中新增連結
接著,請為應用程式中的每個頁面加入 <link rel="manifest"...,藉此告知瀏覽器資訊清單。請在 index.html 檔案的 <head> 元素中加入以下這行文字。
public/index.html
<!-- CODELAB: Add link rel manifest -->
<link rel="manifest" href="/manifest.json">
開發人員工具:移除內容
開發人員工具可讓您快速查看 manifest.json 檔案。在應用程式面板上開啟資訊清單窗格。如果您已正確新增資訊清單資訊,您將可以在這個窗格中查看剖析結果,並以使用者可理解的格式顯示。

新增 iOS 中繼標記 &&;; 圖示
iOS 版 Safari 不支援網頁應用程式資訊清單 (不過),因此您必須在檔案的 <head> 中新增傳統 meta 標記:
public/index.html
<!-- CODELAB: Add iOS meta tags and icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Weather PWA">
<link rel="apple-touch-icon" href="/images/icons/icon-152x152.png">
額外說明:輕鬆完成 Lighthouse 修正
我們的 Lighthouse 稽核列舉了幾個非常容易解決的事情,讓我們來照顧我們。
設定中繼說明
根據 SEO 的稽核,Lighthouse 注意到「Google 文件沒有中繼說明」。說明可以顯示在 Google 搜尋結果中。高品質且獨特的說明可以提高搜尋結果與使用者的搜尋的關聯性,進而增加您的搜尋流量。
如要新增說明,請在文件的 <head> 中加入下列 meta 標記:
public/index.html
<!-- CODELAB: Add description here -->
<meta name="description" content="A sample weather app">
設定網址列主題顏色
在 PWA 稽核中,Lighthouse 注意到我們的應用程式「並未設定網址列主題的顏色」。這時,你可以根據瀏覽器的色彩來設定瀏覽器的網址列,藉此營造更身歷其境的使用者體驗。
如要在行動裝置上設定主題顏色,請將下列 meta 標記新增至文件的 <head>:
public/index.html
<!-- CODELAB: Add meta theme-color -->
<meta name="theme-color" content="#2F3BA2" />
使用 Lighthouse 驗證變更
再次執行 Lighthouse (按一下「稽核」窗格左上角的 [+] 符號) 並驗證變更。
搜尋引擎最佳化 (SEO) 稽核
- Blobstore PASSED:文件有中繼說明。
漸進式網頁應用程式稽核
- 減少失敗:目前的網頁無法在離線狀態下傳回 200 錯誤。
- 減少失敗:
start_url在離線時不會傳回 200 代碼。 - 單純失敗:未註冊控制網頁和
start_url.的服務業者 - Blobstore 已通過檢查:網路應用程式資訊清單符合安裝規定。
- Blobstore PASSED:針對自訂啟動畫面進行設定。
- MODE PASSED:設定網址列主題的顏色。
使用者預期安裝應用程式後,只要離線就能持續享有基準體驗。為什麼這對網頁安裝式網頁應用程式而言至關重要,而無法顯示 Chrome 離線的恐龍遊戲。不論是離線的頁面,還是先前快取資料的唯讀體驗,或是能完全離線的連線時自動同步處理的離線體驗,都適用離線體驗。
在本節中,我們將在氣象應用程式中新增簡易的離線網頁。如果使用者嘗試在離線時載入應用程式,則會顯示我們的自訂網頁,而不是瀏覽器顯示的一般離線網頁。本節最後,我們的天氣應用程式通過下列稽核:
- 目前網頁無法在離線時傳回 200 回應。
start_url不會在離線時收到 200 回應。- 未註冊可控制網頁和
start_url.的服務業者
在下一節中,我們會以完整的離線體驗取代自訂網頁。這麼做可以改善離線體驗,但更重要的是能大幅提升效能,因為大部分的素材資源 (HTML、CSS 和 JavaScript) 皆會在本機儲存並放送,如此一來,聯播網即可成為潛在瓶頸。
讓服務人員受阻
如果您不熟悉服務業者,請參閱「服務工作人員簡介」一文,瞭解其服務項目、生命週期和其他功能的基本知識。
透過 Service Worker 提供的功能應視為漸進式增強功能,且只有在瀏覽器支援時才會新增。以服務工作站為例,您可以快取應用程式的應用程式命令介面和資料,即使網路未連線也一樣。如果服務業者不支援服務,系統就不會呼叫離線程式碼,而使用者可獲得基本體驗。使用功能偵測來提供漸進增強的強化工作,幾乎不會有太多負擔,而且在不支援這項功能的舊式瀏覽器上也不會有中斷。
註冊 Service Worker
第一個步驟是註冊 Service Worker。請將下列程式碼新增到 index.html 檔案中:
public/index.html
// CODELAB: Register service worker.
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js')
.then((reg) => {
console.log('Service worker registered.', reg);
});
});
}
這個程式碼會檢查 Service Worker API API 是否可用,如果 /service-worker.js 服務載入後,則 /service-worker.js 的服務工作人員已經註冊完成。
請注意,服務工作站的來源是根目錄,而非 /scripts/ 目錄。這是設定服務工作站 scope 最簡單的方法。服務工作人員的 scope 會決定服務工作站控制的檔案,也就是服務工作人員攔截要求的路徑。預設的 scope 是 Service Worker 檔案的位置,並延伸至下方的所有目錄。因此,如果 service-worker.js 位於根目錄,則 Service Worker 會控制此網域中所有網頁發出的要求。
預先快取離線網頁
首先,我們必須告知服務人員要快取的內容。我們已經建立了一個簡易的離線頁面 (public/offline.html),當沒有網路連線時,系統都會顯示該頁面。
在您的 service-worker.js 中,在 FILES_TO_CACHE 陣列中加入 '/offline.html',,最終結果應如下所示:
public/service-worker.js
// CODELAB: Add list of files to cache here.
const FILES_TO_CACHE = [
'/offline.html',
];
接著,您需要在 install 事件中加入下列程式碼,通知服務人員預先快取離線網頁:
public/service-worker.js
// CODELAB: Precache static resources here.
evt.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
console.log('[ServiceWorker] Pre-caching offline page');
return cache.addAll(FILES_TO_CACHE);
})
);
我們的 install 事件現在會使用 caches.open() 開啟快取,並提供快取名稱。提供快取名稱可讓我們進行版本管理,或將資料與快取資源分開處理,這樣我們就可以輕鬆地更新其中一個檔案,但不對另一個檔案造成影響。
快取開啟後,我們就能呼叫 cache.addAll(),從清單中擷取網址,並從伺服器擷取網址並將回應新增至快取。請注意,如果個別要求失敗,cache.addAll() 就會失敗。這表示您可以保證,如果安裝步驟成功,快取狀態就會一致。不過,如果系統因為某種原因而失敗,下次服務工作人員啟動時,系統便會自動重試。
開發人員工具:移除內容
讓我們來看看如何使用開發人員工具瞭解服務工作站並進行偵錯。在重新載入網頁之前,請開啟 DevTools,然後前往「Application」(應用程式) 面板上的「Service Workers」(服務工作站) 窗格。它看起來應該像這樣:

如果出現空白網頁,代表目前開啟的頁面沒有任何已註冊的 Service Worker。
現在,請重新載入頁面。「服務工作站」窗格現在會顯示如下內容:

看到以上資訊時,就表示該頁面有執行中的 Service Worker。
在「狀態」標籤旁邊,顯示一個數字 (在本例中為 34251)。請隨時留意這個號碼。可輕鬆判斷服務業者是否已更新。
清理舊的離線網頁
我們會使用 activate 事件清理快取中的所有舊資料。此程式碼可確保您的 Service Worker 會在任何應用程式殼層檔案變更時更新快取。因此,您必須在 Service Worker 檔案頂端遞增 CACHE_NAME 變數,才能成功操作。
請將下列程式碼加進 activate 活動:
public/service-worker.js
// CODELAB: Remove previous cached data from disk.
evt.waitUntil(
caches.keys().then((keyList) => {
return Promise.all(keyList.map((key) => {
if (key !== CACHE_NAME) {
console.log('[ServiceWorker] Removing old cache', key);
return caches.delete(key);
}
}));
})
);
開發人員工具:移除內容
開啟 Service Workers 窗格後,重新整理頁面。您隨即會看到新安裝的 Service Worker 和狀態號碼已遞增。

我們的 install 事件以 self.skipWaiting() 完成,activate 事件的結尾為 self.clients.claim(),因此更新的服務工作站會立即執行。如果沒有這些標記,只要有開啟分頁的分頁,舊的服務工作人員就會持續控制網頁。
處理失敗的網路要求
最後,我們需要處理 fetch 個事件。我們將使用「網路備用快取」策略。Service Worker 會先嘗試從網路擷取資源。如果失敗,則 Service Worker 會從快取傳回離線網頁。

public/service-worker.js
// CODELAB: Add fetch event handler here.
if (evt.request.mode !== 'navigate') {
// Not a page navigation, bail.
return;
}
evt.respondWith(
fetch(evt.request)
.catch(() => {
return caches.open(CACHE_NAME)
.then((cache) => {
return cache.match('offline.html');
});
})
);
fetch 處理常式只須處理網頁導覽作業,因此其他要求可以從「傾印」中移除,並由瀏覽器正常處理。不過,如果要求 .mode 為 navigate,請使用 fetch 嘗試從網路取得項目。如果失敗,catch 處理常式會以 caches.open(CACHE_NAME) 開啟快取,並使用 cache.match('offline.html') 取得預先快取的離線網頁。然後使用 evt.respondWith() 將結果傳回瀏覽器。
開發人員工具:移除內容
進行檢查,確保一切運作正常。開啟「Service Workers」(服務工作站) 窗格時,重新整理頁面。您隨即會看到新安裝的 Service Worker 和狀態號碼已遞增。
也可以查看系統快取的內容。前往開發人員工具的「應用程式」面板上的「快取儲存空間」窗格。在 [Cache Storage] (快取儲存空間) 上按一下滑鼠右鍵,選擇 [Refresh Caches] (重新整理快取),然後展開區段。您應該會看到在左側的靜態快取名稱。按一下快取名稱即可查看所有已快取的檔案。

現在,我們來測試離線模式。返回開發人員工具「應用程式」面板中的「服務工作站」窗格,然後勾選 [離線] 核取方塊。檢查完畢後,「Network」(網路) 面板標籤旁邊應該會顯示一個黃色的警告圖示。這個圖示代表您已經離線。

重新載入網頁,它就能正常運作!我們把 panda 帶到這裡,
測試服務工作站的訣竅
對服務工作人員進行偵錯並非易事,如果系統需要快取,那麼如果快取作業未如預期更新,可能會變得更嚴重。在一般的服務工作站生命週期和程式碼中的錯誤之間,您可能會感到不滿。但不要。
使用開發人員工具
在「應用程式」面板的「服務工作站」窗格中,您可以勾選一些核取方塊,讓生活更加便利。

- 離線:勾選這個選項後,系統便會模擬離線體驗,並禁止對方連上網路。
- 重新載入時更新 - 勾選這個選項後,即可取得最新的 Service Worker、安裝程式並立即啟用。
- 略過網路:勾選這個選項時,要求會略過服務工作站,並直接傳送到網路。
展開新鮮事
在某些情況下,您可能會發現自己已載入快取資料,或是資料未如預期更新。如要清除所有儲存的資料 (localStorage、已建立索引的資料庫資料、快取檔案) 並移除任何服務工作站,請使用「應用程式」面板中的 [清除儲存空間] 窗格。或者,您也可以使用無痕式視窗處理作業。

其他訣竅:
- 取消註冊服務工作站後,服務人員仍會繼續列出該清單,直到其關閉的瀏覽器視窗關閉為止。
- 如果應用程式開啟了多個視窗,則必須先重新載入所有視窗並更新至最新的 Service Worker,新的服務工作站才會生效。
- 取消註冊服務工作站並不會清除快取!
- 如果已有 Service Worker,且新服務工作站已登錄完畢,您必須重新載入,否則當您等待頁面重新載入後,新的服務工作站才會掌控。
使用 Lighthouse 驗證變更
再次執行 Lighthouse 並確認變更。驗證變更之前,別忘了取消勾選 [離線] 核取方塊!
搜尋引擎最佳化 (SEO) 稽核
- Blobstore PASSED:文件有中繼說明。
漸進式網頁應用程式稽核
- MODE PASSED:目前網頁在離線時會以 200 回應。
- Blobstore 已通過驗證:
start_url會在離線時收到 200 回應。 - MODE PASSED:註冊控制網頁和
start_url.的服務人員 - Blobstore 已通過檢查:網路應用程式資訊清單符合安裝規定。
- Blobstore PASSED:針對自訂啟動畫面進行設定。
- MODE PASSED:設定網址列主題的顏色。
花點時間將手機開啟飛航模式,然後嘗試執行一些你喜愛的應用程式。幾乎在所有情況下,這些程式都能提供非常可靠的離線體驗。使用者期望應用程式能提供絕佳的使用體驗。而且網路世界應該不會有差異。漸進式網路應用程式應以離線方式設計。
服務工作站生命週期
服務工作站的生命週期是最複雜的部分。如果你不知道它是怎麼做的,而且有益什麼,它可能就像它正在戰鬥你。不過,只要您瞭解這項工具的運作方式,就能為使用者提供順暢而流暢的更新功能,結合網路和最佳模式。
install 個活動
服務工作人員第一個接收的事件為 install。工作站開始執行後,系統就會立即觸發這個服務,且每個服務工作站只會呼叫一次。如果您變更了 Service Worker 指令碼,瀏覽器會將該指令碼視為不同的服務業者,這樣就會得到自己的 install 事件。

install 事件通常用於快取執行應用程式所需的一切資訊。
activate 個活動
服務工作人員每次啟動時都會收到 activate 事件。activate 事件的主要用途是設定服務工作人員的行為、清除先前執行作業中剩餘的任何資源 (例如舊的快取),並讓服務工作站準備好處理網路要求 (例如下方所述的 fetch 事件)。
fetch 個活動
擷取事件可讓服務工作站攔截任何網路要求及處理要求。網路可用於取得資源、可透過其快取來擷取資源、產生自訂回應,或是任意數量不同的選項。請參閱離線教戰手冊,瞭解有哪些不同的策略可用。
更新 Service Worker
瀏覽器會檢查每次網頁載入時是否有新版的 Service Worker。如果找到新版本,系統會在背景下載並安裝新版本,但尚未啟用。您的 Service Worker 版本會處於等待狀態,直到沒有任何開啟使用舊版 Service Worker 的頁面為止。所有使用舊版 Service Worker 的視窗都關閉後,新的 Service Worker 就會啟用並可以控管。詳情請參閱 Service Worker 生命週期生命週期文件的更新服務工作站一節。
選擇合適的快取策略
選擇適合的快取策略取決於您要快取的資源類型,以及您日後需要存取該資源的方式。以我們的天氣應用程式來說,我們將需要快取的資源分成兩類:我們想要預先快取的資源,以及我們可在執行階段快取的資料。
快取靜態資源
取得資源類似使用者安裝桌面或行動應用程式時的概念。裝置執行或快取應用程式所需的主要資源會安裝到裝置上,以便日後在網路連線時順利載入資源。
至於我們的應用程式,我們會在安裝 Service Worker 時預先快取所有靜態資源,以便執行應用程式所需的一切資料都儲存在使用者的裝置上。為了確保我們的應用程式能飛快載入,我們將使用快取優先策略,而不是從網路取得資源,而是從本機快取中擷取資源;只有在無法使用時,才會嘗試從網路取得該資源。

從本機快取提取會排除任何網路變異。不管用戶在哪裡的網絡中(WiFi、5G、3G 甚至 2G),我們需要執行的密鑰數據很快就可用。
快取應用程式資料
過時式策略適合某些類型的資料,適用於我們的應用程式。它會盡快在畫面中顯示資料,並在網路傳回最新資料後更新。「過時的重新驗證」代表我們必須啟動兩個非同步要求,一個是對快取,另一個則是對網路。

在正常情況下,系統會立即傳回快取資料,提供應用程式能使用的最新資料。接著,當網路要求傳回時,應用程式就會使用來自網路的最新資料進行更新。
我們的應用程式能提供比一般網路更好的使用體驗,因此可以改用快取策略,因為使用者不必等到網路要求逾時,就能看見在螢幕上顯示的內容。他們一開始可能會看到較舊的資料,但一旦網路要求傳回,應用程式就會以最新資料進行更新。
更新應用程式邏輯
如前所述,應用程式需要啟動兩個非同步要求,一個對快取執行,一個向網路發出。這個應用程式使用 window 中提供的 caches 物件來存取快取並擷取最新資料。這項功能是漸進式強化的絕佳範例,因為 caches 物件可能無法在部分瀏覽器中使用。如果沒有,網路要求仍可正常運作。
更新 getForecastFromCache() 函式,檢查 caches 物件是否可在全域 window 物件中取得,以及是否從快取要求資料。
public/scripts/app.js
// CODELAB: Add code to get weather forecast from the caches object.
if (!('caches' in window)) {
return null;
}
const url = `${window.location.origin}/forecast/${coords}`;
return caches.match(url)
.then((response) => {
if (response) {
return response.json();
}
return null;
})
.catch((err) => {
console.error('Error getting data from cache', err);
return null;
});
接著,我們必須修改 updateData(),使其發出兩次呼叫;一個是從 getForecastFromNetwork() 取得聯播網預測,另一個是 getForecastFromCache(),以取得最新的快取預測:
public/scripts/app.js
// CODELAB: Add code to call getForecastFromCache.
getForecastFromCache(location.geo)
.then((forecast) => {
renderForecast(card, forecast);
});
我們的氣象應用程式現在提出兩個非同步的資料要求,一個是來自快取,另一個則是透過 fetch。如果快取中有資料,則會傳回該 資料,並迅速轉譯 (顯示數十毫秒)。接著,當 fetch 回應時,系統會從氣象 API 直接更新卡片,以更新卡片資料。
請注意快取要求和 fetch 要求如何以呼叫更新預測資訊卡的方式結束。應用程式如何確認裝置是否已顯示最新資料?系統會透過以下程式碼在 renderForecast() 中進行處理:
public/scripts/app.js
// If the data on the element is newer, skip the update.
if (lastUpdated >= data.currently.time) {
return;
}
每次卡片更新時,應用程式會將資料時間戳記儲存在卡片的隱藏屬性上。如果卡片上已有的時間戳記比傳遞至函式的資料更新,應用程式才會啟動。
預先快取應用程式資源
在 Service Worker 中,新增 DATA_CACHE_NAME 來區分應用程式與應用程式殼層的資料。當應用程式殼層更新且舊快取已清除完畢時,我們的資料仍會保持不變,並可快速載入資料。請注意,如果您的資料格式未來有所變動,您就需要解決這個問題,並確保應用程式殼層和內容保持同步。
public/service-worker.js
// CODELAB: Update cache names any time any of the cached files change.
const CACHE_NAME = 'static-cache-v2';
const DATA_CACHE_NAME = 'data-cache-v1';
別忘了一併更新「CACHE_NAME」;我們也將變更所有的靜態資源。
為了讓應用程式能夠離線運作,我們必須預先快取所有需要的資源。這也有助於我們改善成效。應用程式不需從網路取得所有資源,因此能夠從本機快取載入所有資源,免去任何網路不穩定的情形。
使用檔案清單更新 FILES_TO_CACHE 陣列:
public/service-worker.js
// CODELAB: Add list of files to cache here.
const FILES_TO_CACHE = [
'/',
'/index.html',
'/scripts/app.js',
'/scripts/install.js',
'/scripts/luxon-1.11.4.js',
'/styles/inline.css',
'/images/add.svg',
'/images/clear-day.svg',
'/images/clear-night.svg',
'/images/cloudy.svg',
'/images/fog.svg',
'/images/hail.svg',
'/images/install.svg',
'/images/partly-cloudy-day.svg',
'/images/partly-cloudy-night.svg',
'/images/rain.svg',
'/images/refresh.svg',
'/images/sleet.svg',
'/images/snow.svg',
'/images/thunderstorm.svg',
'/images/tornado.svg',
'/images/wind.svg',
];
由於我們是手動產生要快取的檔案清單,因此每次更新檔案時,我們「都必須」更新 CACHE_NAME。我們已經將 offline.html 從快取檔案清單中移除,因為我們的應用程式目前已包含離線運作所需的一切必要資源,且不會再顯示離線頁面。
更新啟用事件處理常式
為確保我們的 activate 事件不會意外刪除我們的資料,請在 service-worker.js 的 activate 事件中,將 if (key !== CACHE_NAME) { 替換為:
public/service-worker.js
if (key !== CACHE_NAME && key !== DATA_CACHE_NAME) {
更新擷取事件處理常式
我們需要修改 Service Worker,以攔截攔截到 Maps API 的要求,並將回應儲存在快取中,方便日後存取。在過時的重新驗證策略中,我們預期網路回應將成為「事實的資料來源」,並且隨時向我們提供最新資訊。如果網路可以正常運作,因為我們尚未在應用程式中擷取最新快取資料,所以是失敗的。
更新 fetch 事件處理常式,以與其他資料分開處理對資料 API 的要求。
public/service-worker.js
// CODELAB: Add fetch event handler here.
if (evt.request.url.includes('/forecast/')) {
console.log('[Service Worker] Fetch (data)', evt.request.url);
evt.respondWith(
caches.open(DATA_CACHE_NAME).then((cache) => {
return fetch(evt.request)
.then((response) => {
// If the response was good, clone it and store it in the cache.
if (response.status === 200) {
cache.put(evt.request.url, response.clone());
}
return response;
}).catch((err) => {
// Network request failed, try to get it from the cache.
return cache.match(evt.request);
});
}));
return;
}
evt.respondWith(
caches.open(CACHE_NAME).then((cache) => {
return cache.match(evt.request)
.then((response) => {
return response || fetch(evt.request);
});
})
);
程式碼會攔截要求,並檢查要求是否提供天氣預報。如果是的話,請使用 fetch 提出要求。傳回回應後,請開啟快取並複製回應,然後將回應儲存在快取中,然後將回應傳回原始要求者。
我們需要我們的服務工作人員處理所有要求 (包括圖片、指令碼、CSS 檔案等等),而不只是導覽作業,因此我們必須移除 evt.request.mode !== 'navigate' 檢查功能。如果我們不執行檢查工作,則只有服務工作站快取提供 HTML。所有其他內容則需透過聯播網請求。
實戰演練
應用程式現在應該可以完全離線運作。請重新整理頁面,確保已安裝最新的 Service Worker。然後儲存幾個城市,並按下應用程式中的「重新整理」按鈕,取得最新的天氣資料。
接著,前往開發人員工具的「應用程式」面板的「快取儲存空間」窗格。展開這個部分,您應該會看到左側的靜態快取和資料快取名稱列在左側。開啟資料快取,應該顯示各城市儲存的資料。

切換至「Service Workers」(服務工作站) 窗格,然後勾選 [離線] 核取方塊。請嘗試重新載入頁面,然後離線並重新載入網頁。
如果你連上的是速度飛快的網路,並想查看連線速度緩慢時的預測天氣情況,請將 server.js 中的 FORECAST_DELAY 屬性設為 5000。所有傳送至預測 API 的要求都會延遲 5000 毫秒。
使用 Lighthouse 驗證變更
再次執行 Lighthouse 也是個好主意。
搜尋引擎最佳化 (SEO) 稽核
- Blobstore PASSED:文件有中繼說明。
漸進式網頁應用程式稽核
- MODE PASSED:目前網頁在離線時會以 200 回應。
- Blobstore 已通過驗證:
start_url會在離線時收到 200 回應。 - MODE PASSED:註冊控制網頁和
start_url.的服務人員 - Blobstore 已通過檢查:網路應用程式資訊清單符合安裝規定。
- Blobstore PASSED:針對自訂啟動畫面進行設定。
- MODE PASSED:設定網址列主題的顏色。
安裝漸進式網路應用程式後,應用程式的外觀和行為將與其他所有已安裝的應用程式相同。從其他應用程式啟動的同一個位置啟動應用程式。應用程式可在沒有網址列或其他瀏覽器 UI 的應用程式中執行。如同所有其他已安裝的應用程式,它也是工作切換器中的頂層應用程式。

在 Chrome 中,您可以透過三點圖示內容選單安裝漸進式網頁應用程式,也可以為使用者提供按鈕或其他 UI 元件,提示使用者安裝應用程式。
使用 Lighthouse 稽核
應用程式必須符合特定條件,使用者才能安裝漸進式網頁應用程式。最簡單的方法就是使用 Lighthouse,並確保符合可安裝的條件。

如果您已完成這個程式碼研究室,您的 PWA 應該已經符合這些條件。
將 install.js 新增至 index.html
首先,請將 install.js 新增至 index.html 檔案。
public/index.html
<!-- CODELAB: Add the install script here -->
<script src="/scripts/install.js"></script>
監聽 beforeinstallprompt 事件
如果符合主畫面「條件」,Chrome 就會啟動 beforeinstallprompt 事件,讓您指出應用程式可以「安裝」並提示使用者安裝。加入下列程式碼來監聽 beforeinstallprompt 事件:
public/scripts/install.js
// CODELAB: Add event listener for beforeinstallprompt event
window.addEventListener('beforeinstallprompt', saveBeforeInstallPromptEvent);
儲存活動並顯示安裝按鈕
在 saveBeforeInstallPromptEvent 函式中,我們會儲存對 beforeinstallprompt 事件的參照,以便稍後在其上呼叫 prompt(),並更新 UI 以顯示安裝按鈕。
public/scripts/install.js
// CODELAB: Add code to save event & show the install button.
deferredInstallPrompt = evt;
installButton.removeAttribute('hidden');
顯示提示並隱藏按鈕
當使用者按一下安裝按鈕時,我們必須為儲存的 beforeinstallprompt 事件呼叫 .prompt()。此外,由於每個儲存的事件只能呼叫 .prompt(),因此我們也必須隱藏安裝按鈕。
public/scripts/install.js
// CODELAB: Add code show install prompt & hide the install button.
deferredInstallPrompt.prompt();
// Hide the install button, it can't be called twice.
evt.srcElement.setAttribute('hidden', true);
呼叫 .prompt() 後,向使用者顯示一個對話方塊,請使用者將應用程式新增到主畫面。
記錄結果
您可以透過監聽已儲存 beforeinstallprompt 事件 userChoice 屬性傳回的承諾使用合約,瞭解使用者對回應對話方塊的回應方式。這項保證會在系統顯示提示,且使用者已回應之後,傳回包含 outcome 屬性的物件。
public/scripts/install.js
// CODELAB: Log user response to prompt.
deferredInstallPrompt.userChoice
.then((choice) => {
if (choice.outcome === 'accepted') {
console.log('User accepted the A2HS prompt', choice);
} else {
console.log('User dismissed the A2HS prompt', choice);
}
deferredInstallPrompt = null;
});
有關 userChoice 的註解:規格會將其定義為屬性,而不是您預期的函式。
記錄所有安裝事件
除了加入用來安裝應用程式的使用者介面之外,使用者也可以透過其他方式安裝 PWA,例如 Chrome 的三點選單。如要追蹤這些事件,請監聽已安裝的事件。
public/scripts/install.js
// CODELAB: Add event listener for appinstalled event
window.addEventListener('appinstalled', logAppInstalled);
然後,我們需要更新 logAppInstalled 函式。在這個程式碼研究室中,我們只使用 console.log,但在實際執行應用程式中,建議您使用分析軟體來記錄事件。
public/scripts/install.js
// CODELAB: Add code to log the event
console.log('Weather App was installed.', evt);
更新 Service Worker
別忘了對已經快取的檔案進行變更,以免更新「service-worker.js」檔案中的「CACHE_NAME」。只要在開發人員工具的「應用程式」面板的「服務工作站」面板中勾選 [略過網路] 核取方塊,即可進行開發,但實際上對現實世界沒有幫助。
實戰演練
讓我們來看看安裝步驟。為了安全起見,請使用開發人員工具「應用程式」面板中的 [清除網站資料] 按鈕清除所有資料,並確認系統已經開始更新。如果您先前已安裝該應用程式,請務必解除安裝,否則「安裝」圖示將不會再顯示。
確認系統顯示安裝按鈕
首先,讓我們確認安裝圖示是否正確顯示。請務必在電腦和行動裝置上嘗試這項操作。
- 在新的 Chrome 分頁中開啟網址。
- 開啟 Chrome 的三點選單 (位於網址列旁邊)。
▢ 確認選單中顯示「安裝天氣...」。 - 使用右上角的重新整理按鈕重新整理天氣資料,確保符合我們的使用者參與經驗法則。
▢ 確認應用程式的標頭中顯示安裝圖示。
確認安裝按鈕是否正常運作
接下來,我們要確認一切皆已正確安裝,且我們的事件已經正確觸發。使用電腦版或行動版都可以。如果您想在行動裝置上測試這項功能,請確認您已經使用遠端偵錯功能,以便查看控制台中記錄的內容。
- 開啟 Chrome,然後在新的瀏覽器分頁中前往 Weather PWA。
- 開啟開發人員工具並切換到主控台面板。
- 按一下右上角的安裝按鈕。
▢ 確認安裝按鈕已消失
▢ 確認系統顯示安裝模式對話方塊。 - 按一下 [取消]。
▢ 確認 [使用者已關閉 A2HS 提示] (顯示在控制台輸出內容中)。
▢ 確認系統是否顯示安裝按鈕。 - 再按一下 [安裝] 按鈕,然後點選模式對話方塊中的安裝按鈕。
▢ 確認主控台輸出結果顯示「使用者已接受 A2HS 提示」。
▢ 確認主控台輸出結果顯示「已安裝天氣應用程式」。
▢ 確認「天氣」應用程式已新增至應用程式通常執行的位置。 - 啟動氣象 PWA。
▢ 將應用程式開啟為獨立應用程式,可以是電腦上的應用程式視窗,或是行動裝置上的全螢幕。
。
確認 iOS 安裝能正常運作
並檢查 iOS 裝置上的行為。如果您有 iOS 裝置,可以使用裝置;如果您使用的是 Mac,請嘗試使用 Xcode 提供的 iOS 模擬工具。
- 開啟 Safari 並在新的瀏覽器分頁中前往 Weather PWA。
- 按一下 [分享]
按鈕。 - 向右捲動,然後按一下 [新增到主畫面] 按鈕。
▢ 確認標題、網址和圖示正確無誤。 - 按一下 [新增]
。瞭解應用程式圖示是否已新增到主畫面。 - 從主畫面啟動天氣 PWA。
▢ 確認應用程式以全螢幕啟動。
獎勵:偵測應用程式是否從主畫面啟動
display-mode 媒體查詢可讓您根據應用程式的啟動方式套用樣式,或決定 JavaScript 啟動樣式的方式。
@media all and (display-mode: standalone) {
body {
background-color: yellow;
}
}
此外,您也可以查看 JavaScript 中的 display-mode 媒體查詢,瞭解您是否在獨立環境中執行。
獎勵:解除安裝 PWA
提醒您,如果已安裝應用程式,「beforeinstallevent」就不會觸發,所以在開發過程中,您可能需要多次安裝及解除安裝應用程式,以確保一切運作正常。
Android
在 Android 裝置上,解除安裝的方式與解除安裝其他應用程式的方式相同。
- 開啟應用程式導覽匣。
- 向下捲動,找出「天氣」圖示。
- 將應用程式圖示拖曳到螢幕頂端。
- 選擇 [解除安裝]。
Chrome 作業系統
在 Chrome 作業系統中,您可以輕鬆透過啟動器搜尋框解除安裝 PWA。
- 開啟啟動器。
- 在搜尋框中輸入「Weather」(天氣) ,您的 PWA 就會出現在搜尋結果中。
- 在氣象 PWA 上按一下滑鼠右鍵 (使用滑鼠右鍵)。
- 按一下 [從 Chrome 中移除...]。
macOS 和 Windows
在 Mac 和 Windows 上,Chrome 可能會解除安裝 PWA:
- 在新的瀏覽器分頁中,開啟 chrome://apps。
- 在氣象 PWA 上按一下滑鼠右鍵 (使用滑鼠右鍵)。
- 按一下 [從 Chrome 中移除...]。
您也可以開啟已安裝的 PWA,按一下右上角的三點內容選單,然後選擇 [解除安裝氣象 PWA...]。
恭喜!您已成功建立第一個漸進式網頁應用程式!
您已新增網頁資訊清單來啟用資訊清單,且您已新增 Service Worker,以確保 PWA 保持快速且可靠。您已經學習如何使用開發人員工具來稽核應用程式,以及如何利用這項工具改善使用者體驗。
現在你已知道將任何網路應用程式轉換成漸進式網頁應用程式的關鍵步驟。
後續步驟
查看一些程式碼研究室…