前言:今天結識了google PWA提供的一個對移動端Web應用提供離線體驗的一個功能,感受頗有用。我這裏不分享本身的寫法和代碼。官網文檔說的很詳細,直接粘過來你們看吧。css
推薦官網地址:你的第一個漸進式 Web 應用(Progressive Web App - PWA)html
文章詳情以下:node
漸進式 Web 應用會在桌面和移動設備上提供可安裝的、仿應用的體驗,可直接經過 Web 進行構建和交付。它們是快速、可靠的 Web 應用。最重要的是,它們是適用於任何瀏覽器的 Web 應用。若是你在構建一個 Web 應用,其實已經開始構建漸進式 Web 應用了。android
每一個 Web 體驗都必須快速,對於漸進式 Web 應用更是如此。快速是指在屏幕上獲取有意義內容所需的時間,要在不到 5 秒的時間內提供交互式體驗。ios
而且,它必須真的很快。很難形容可靠的高性能有多重要。能夠這樣想: 本機應用的首次加載使人沮喪。應用商店和漫長的下載都是門檻,不過,該前期成本包含了從此該應用的每次啓動時間,一旦你安裝好該應用,這些啓動過程就不會再有額外的延遲。每一個應用的啓動速度都與上一次同樣快,沒有任何差別。已安裝的漸進式 Web 應用必須能讓用戶得到可靠的性能。git
漸進式 Web 應用能夠在瀏覽器選項卡中運行,但也能夠安裝。爲網站添加書籤只是添加了一個快捷方式,但已安裝的漸進式 Web 應用的外觀和行爲會與任何已安裝的應用相似。它與其它應用的啓動位置是同樣的。你能夠控制啓動體驗,包括自定義啓動畫面、圖標等。它在應用窗口中做爲應用運行,沒有地址欄或其它瀏覽器 UI。與其它已安裝的應用同樣,它是任務管理器中的頂級應用。github
請記住,讓可安裝的 PWA 保持快速可靠是相當重要的。安裝 PWA 的用戶但願他們的應用正常運行,不管他們使用何種網絡鏈接。這是每一個已安裝應用必須知足的基線預期。web
使用響應式設計技術,漸進式 Web 應用可在移動和桌面上工做,使用跨平臺的單一代碼庫。若是你正在考慮是否編寫本機應用,請看看 PWA 提供的好處。chrome
在此 codelab 中,你將使用漸進式 Web 應用技術構建天氣 Web 應用。你的應用將:shell
beforeinstallprompt
事件告訴用戶它是可安裝的。
Warning:爲了簡化此 codelab ,並解釋提供離線體驗的基礎知識,咱們使用的是原生 JavaScript。在生產應用中,咱們強烈建議使用 Workbox 工具來構建 Service Worker 。它能夠幫你消除許多可能遇到的坑和死角。
此 codelab 專一於漸進式 Web 應用。屏蔽了不相關的概念和代碼塊,併爲你提供簡單的複製和粘貼。
最近版本的 Chrome(74 或更高版本)
PWA 只是個 Web 應用,所以適用於全部瀏覽器,但咱們將使用 Chrome DevTools 的一些功能來更好地瞭解瀏覽器層面的狀況,並用它來測試安裝體驗。
瞭解 HTML,CSS,JavaScript 和Chrome DevTools 。
咱們的天氣數據來自 Dark Sky API。要使用它,你須要申請 API 密鑰。它很容易使用,而且能夠免費用於非商業項目。
Note: 你還能夠在沒有 Dark Sky API 密鑰的狀況下完成此 codelab。若是咱們的服務器沒法從 Dark Sky API 獲取真實數據,它將返回假數據。
要測試你的 API 密鑰是否正常工做,請向 DarkSky API 發出 HTTP 請求。修改如下網址,將 DARKSKY_API_KEY
替換爲你的 API 密鑰。若是一切正常,你應該看到紐約市的最新天氣預報。
https://api.darksky.net/forecast/DARKSKY_API_KEY/40.7720232,-73.9732319
咱們已將此項目所需的一切都放入 Git 倉庫中。首先,你須要獲取代碼並在你喜歡的開發環境中打開它。對於此代碼庫,咱們建議使用 Glitch。
推薦用 Glitch 來使用此代碼庫。
.env
文件,並使用 DarkSky API 密鑰來更新它。若是你想下載代碼並在本地工做,你須要安裝好最新版本的 Node 和代碼編輯器。
Caution: 若是你在本地工做,某些 Lighthouse 審計可能沒法經過,甚至可能沒法安裝,由於本地服務器並無在安全環境下運行。
npm install
以安裝運行服務器所需的依賴項。server.js
並設置 DarkSky API 密鑰。node server.js
以在端口 8000 上啓動服務器.咱們的起點是爲此 codelab 設計的基本天氣應用。代碼已經大幅簡化,以突顯此代碼庫中的概念,而且它幾乎沒有作錯誤處理。若是你選擇在生產應用中複用此代碼,請確保處理各類錯誤並徹底測試全部代碼。
咱們將試着......
server.js
中的 FORECAST_DELAY
爲天氣預報服務器添加延遲Lighthouse是一款易於使用的工具,可幫助你提升網站和網頁的質量。它可用來對性能,可訪問性,漸進式 Web 應用等進行審計。每種審計及都有一個參考文檔,解釋了該審計爲什麼重要,以及如何解決所發現的問題。
咱們將使用 Lighthouse 來審計咱們的天氣應用,並驗證咱們所作的更改。
Note: 你能夠在 Chrome DevTools 中,以命令行或 Node 模塊的方式運行 Lighthouse。考慮將Lighthouse 添加到你的構建流程中,以確保你的 Web 應用不會出現迴歸問題。
咱們將重點關注漸進式 Web 應用的審計結果。
這裏有不少紅色信息須要關注:
start_url
未給出 200 響應。start_url
的 Service Worker。讓咱們進入並開始修復其中的一些問題!
到本節結束時,咱們的天氣應用將經過如下審計:
Web 應用清單是一個簡單的 JSON 文件,它使開發人員可以控制本應用對用戶的顯示方式。
使用 Web 應用清單,你的 Web 應用能夠:
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"
}
本清單可支持一組用於不一樣屏幕尺寸的圖標。對於此 codelab ,咱們還包含了一些其它的,由於咱們須要把它們集成進 iOS。
Note: 若是想安裝它,Chrome 會要求你至少提供 192x192px 圖標和 512x512px 圖標。可是你也能夠提供其它尺寸。 Chrome 會使用最接近 48dp 的圖標,例如,2x 設備上的 96px 或 3x 設備的 144px。
接下來,咱們須要經過嚮應用中的每一個頁面添加 <link rel="manifest"...
來把此清單告知瀏覽器。把下列代碼行添加到 index.html
文件中的 <head>
元素下。
<!-- CODELAB: Add link rel manifest -->
<link rel="manifest" href="/manifest.json">
DevTools 提供了一種快速簡便的方法來檢查你的 manifest.json
文件。打開 Application 面板上的 Manifest 窗格。若是你已正確添加清單信息,你將可以在此窗格中看到它以對人類友好的格式進行解析和顯示。
iOS 上的 Safari 不支持 Web 應用清單( 至少目前爲止 ),所以你須要將傳統的 meta
標籤添加到 index.html
文件的 <head>
中:
<!-- 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 審計還提出了一些其它很容易解決的問題,因此讓咱們先來處理下這些問題。
根據 SEO 審計,Lighthouse 提出咱們的 「 Document does not have a meta description. 」。描述能夠顯示在 Google 的搜索結果中。高質量,獨特的描述可使你的搜索結果與搜索用戶更相關,並能夠增長搜索流量。
要添加描述,請將如下 meta
標記添加到文檔的 <head>
中:
<!-- CODELAB: Add description here -->
<meta name="description" content="A sample weather app">
在 PWA 審計中,Lighthouse 提出咱們的應用「 Does not set an address-bar theme color 」。將瀏覽器的地址欄設置爲與你的品牌色相匹配,能夠提供更加沉浸式的用戶體驗。
要在移動設備上設置主題顏色,請將如下 meta
標記添加到文檔的 <head>
中:
<!-- CODELAB: Add meta theme-color -->
<meta name="theme-color" content="#2F3BA2" />
再次運行 Lighthouse(經過單擊「審計」窗格左上角的+號)並驗證你的更改。
SEO 審計
*✅經過: 文檔已有元描述。
漸進式應用審計
start_url
未給出 200 響應。start_url
的 Service Worker。 *✅經過: Web 應用清單符合可安裝性要求。 *✅經過: 已配置自定義閃屏。 *✅經過: 已設置地址欄的主題顏色。用戶會期待所安裝的應用在離線時始終具備基線體驗。所以對於可安裝的 Web 應用來講,永遠不會顯示 Chrome 的離線恐龍圖是相當重要的。離線體驗的範圍包括簡單的離線頁面、具備先前緩存數據的只讀體驗,甚至具備徹底功能的離線體驗(在網絡鏈接恢復時自動同步)。
在本節中,咱們將向天氣應用添加一個簡單的離線頁面。若是用戶在離線時嘗試加載應用,則會顯示咱們的自定義頁面,而不是瀏覽器顯示的典型離線頁面。到本節結束時,咱們的天氣應用將經過如下審計:
start_url
未給出 200 響應。start_url
的 Service Worker。在下一部分中,咱們將使用完整的離線體驗替換咱們的自定義離線頁面。這將改善離線體驗,但更重要的是,它將顯著提升咱們的性能,由於咱們的大多數資產(HTML,CSS 和 JavaScript)都將在本地存儲和提供,從而消除了網絡方面的潛在瓶頸。
若是你對 Service Worker 不熟悉,能夠經過閱讀 Service Workers 簡介來了解它們能夠作什麼,它們的生命週期如何工做等等,從而得到初步的理解。完成此 codelab 後,請務必查看 Debugging Service Workers code lab以便更深刻地瞭解如何與 Service Worker 合做。
經過 Service Worker 提供的功能應被視爲漸進加強功能,而且僅在瀏覽器支持時才添加。例如,對於 Service Worker ,你能夠爲應用緩存應用外殼和數據,以便在網絡不可用時也能使用它。若是不支持 Service Worker ,則不會調用離線代碼,而用戶將得到基本體驗。使用特性檢測來提供漸進加強功能的開銷很小,而且在不支持該功能的舊瀏覽器中不會出錯。
Warning: Service Worker 功能僅在經過 HTTPS 訪問的頁面上可用(http://localhost 及其等價物也可用來協助咱們進行測試)。
第一步是註冊 Service Worker 。將如下代碼添加到 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 是否可用,若是是,則在頁面加載完畢時註冊 /service-worker.js
的 Service Worker。
注意, Service Worker 是從根目錄提供的,而不是從 /scripts/
目錄。這是設置 Service Worker 的 scope
的最簡單方法。Service Worker 的 scope
會決定 Service Worker 能控制哪些文件,換句話說, Service Worker 將攔截哪一個路徑下的請求。默認的 scope
是 Service Worker 文件所在的位置並及其各級目錄。所以,若是 service-worker.js
位於根目錄中,則 Service Worker 將控制對該域中全部網頁的請求。
首先,咱們須要告訴 Service Worker 緩存什麼。咱們已經建立了一個簡單的離線頁面 ( public/offline.html
),只要沒有網絡鏈接,咱們就會顯示它。
在 service-worker.js
中,將 '/offline.html',
添加到 FILES_TO_CACHE
數組中,最終結果應以下所示:
// CODELAB: Update cache names any time any of the cached files change.
const FILES_TO_CACHE = [
'/offline.html',
];
接下來,咱們須要修改 install
事件以告知 Service Worker 預先緩存離線頁面:
// 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);
})
);
Note: Service Worker 的事件和生命週期將在下一節中介紹。
咱們的 install
事件如今使用 caches.open()
打開緩存並傳入緩存名稱。提供緩存名稱能讓咱們對緩存資源進行版本控制或移除,這樣咱們咱們就能輕鬆的修改一個而不影響另外一個。
一旦緩存打開,咱們就能夠調用 cache.addAll()
了,它接受一個 URL 列表,從服務器獲取這些 URL 並將其響應添加到緩存中。請注意,若是任何單個請求失敗,cache.addAll()
就不會生效。這意味着你能夠確保,若是安裝步驟成功了,你的緩存必然處於一致的狀態。可是,若是因爲某種緣由而失敗了,它將在下次 Service Worker 啓動時自動重試。
讓咱們來看看如何使用 DevTools 來理解和調試 Service Worker 。在從新加載頁面以前,打開 DevTools,轉到 Application面板上的 Service Workers 窗格。它應該以下所示:
當你看到這樣的空白頁面時,表示當前打開的頁面中沒有任何已註冊的 Service Worker 。
如今,從新加載頁面,「Service Worker」 窗格應以下所示:
當你看到這樣的信息時,表示該頁面正在運行 Service Worker 。
狀態標籤旁邊有一個數字(這裏是34251),在你使用 Service Worker 時,請密切注意該數字。這是一種判斷你的 Service Worker 是否已更新的簡單方式。
咱們將使用 activate
事件來清理緩存中的任何舊數據。此代碼可確保你的 Service Worker 在任何應用外殼文件發生更改時更新其緩存。爲了使其工做,你須要在 Service Worker 文件的頂部增長 CACHE_NAME
變量。
將如下代碼添加到 activate
事件中:
// 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 Worker」 窗格,刷新頁面,你將看到安裝了新的 Service Worker ,而且狀態編號會遞增。
更新後的 Service Worker 當即得到控制權,由於咱們的 install
事件以 self.skipWaiting()
結束, activate
事件以 self.clients.claim()
結束。若是沒有這些,只要有一個打開着此頁面的選項卡,舊的 Service Worker 就會繼續控制此頁面。
最後,咱們須要處理 fetch
事件。咱們將使用 "網絡優先,回退到緩存" 的策略 。 Service Worker 將首先嚐試從網絡獲取資源,若是失敗,它將從緩存中返回離線頁面。
// 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()
將結果傳回瀏覽器。
Key Point: 把 fetch
調用包裝在 evt.respondWith()
中會阻止瀏覽器的默認處理,並告訴瀏覽器咱們要本身處理該響應。若是你沒有在 fetch
處理程序中調用 evt.respondWith()
,你只會得到默認的網絡行爲。
讓咱們檢查一下,確保一切正常。打開 「Service Worker」 窗格,刷新頁面,你將看到安裝了新的 Service Worker ,而且狀態編號會增長。
咱們還能夠查看已緩存的內容。轉到 DevTools 的 Application 面板上的 Cache Storage 窗格。右鍵單擊 Cache Storage,選擇 Refresh Caches,展開該部分,你應該會在左側看到靜態緩存的名稱。單擊緩存名稱將顯示緩存的全部文件。
如今,咱們來測試下離線模式。返回 DevTools 的 Service Workers 窗格並檢查 Offline 複選框。檢查後,你應該會在 Network 面板選項卡旁邊看到一個黃色警告圖標。這表示你處於離線狀態。
從新加載你的頁面......能夠了!咱們獲得咱們的離線熊貓圖,而不是 Chrome 的離線恐龍圖!
當涉及緩存時,調試 Service Worker 多是一個挑戰,若是緩存未按預期更新,事情可能變成噩夢。在典型的 Service Worker 生命週期和代碼中的錯誤中,你可能會很快感到沮喪。可是,沒必要如此。
在 「Application」 面板的「 Service Worker 」窗格中,有一些複選框可讓你更輕鬆。
在某些狀況下,你可能會發現本身正在加載已緩存的數據或者沒能按預期更新內容。要清除全部已保存的數據(localStorage,indexedDB 數據,緩存文件)並刪除任何 Service Worker ,請使用 「Application」 選項卡中的 「Clear storage」 窗格。或者,你也可使用隱身窗口。
其它提示:
再次運行 Lighthouse 並驗證你的更改。在驗證更改以前,請不要忘記取消 「Offline」 複選框!
SEO 審計
*✅經過: 文檔已有元描述。
漸進式應用審計
*✅經過: 在離線時當前頁面未給出 200 響應。 *✅經過: 在離線時 start_url
未給出 200 響應。 *✅經過: 未註冊用來控制頁面和 start_url
的 Service Worker。 *✅經過: Web 應用清單符合可安裝性要求。 *✅經過: 已配置自定義閃屏。 *✅經過: 已設置地址欄的主題顏色。
將手機開啓飛行模式,而後嘗試運行一些你喜歡的應用。幾乎在全部狀況下,它們都提供了至關強大的離線體驗。用戶但願他們的應用具備穩健的體驗,Web 應用也不例外。漸進式 Web 應用在設計時應該把離線做爲核心場景。
Key Point:離線優先的設計能夠經過減小應用發出的網絡請求數量並改用預先緩存資源並直接從本地緩存提供資源來大幅提升Web 應用的性能。即便用最快的網絡鏈接,從本地緩存提供的服務也仍然會更快!
Service Worker 的生命週期是最複雜的部分。若是你不知道它想要作什麼以及有什麼好處,你可能會以爲它到處和你做對。可是一旦你知道它是如何工做的,你就能夠爲用戶提供無縫、免打擾的更新,將網絡應用和本機應用中最好的一面結合起來。
Key Point:此 codelab 僅涵蓋 Service Worker 生命週期的基礎知識。要深刻了解,請參閱有關 WebFundamentals 的 Service Worker 生命週期 文檔。
install
事件Service Worker 得到的第一個事件是 install
。它會在 Service Worker 執行時當即觸發,而且每一個 Service Worker 只會調用一次。若是你更改了 Service Worker 腳本,瀏覽器就會將其視爲另外一個 Service Worker,而且它將得到本身的 install
事件。
一般, install
事件用於緩存應用運行時所需的所有內容。
activate
事件Service Worker 每次啓動時都會收到 activate
事件。 activate
事件的主要目的是配置 Service Worker 的行爲,清除之前運行中遺留的任何資源(例如舊緩存),並讓 Service Worker 準備好處理網絡請求(例以下面要講的 fetch
事件)。
fetch
事件fetch 事件容許 Service Worker 攔截並處理任何網絡請求。它能夠經過網絡獲取資源、能夠從本身的緩存中提取資源、生成自定義響應,以及不少種不一樣的選擇。查看離線寶典瞭解你可使用的不一樣策略。
瀏覽器會檢查每一個頁面加載時是否有新版本的 Service Worker 。若是找到新版本,則會下載這個新版本並在後臺安裝,但不會激活它。它會處於等待狀態,直到再也不打開任何使用舊 Service Worker 的頁面。一旦關閉了使用舊 Service Worker 的全部窗口,新的 Service Worker 就會被激活並得到控制權。更多詳細信息,請參閱 Service Worker 生命週期文檔中的更新 Service Worker部分。
選擇正確的緩存策略取決於你嘗試緩存的資源類型以及之後可能須要的資源。對於這個天氣應用,須要緩存的資源能夠分爲兩類: 須要預先緩存的資源以及將在運行時緩存的數據。
預先緩存資源與用戶安裝桌面或移動應用時的狀況相似。應用運行所需的關鍵資源已安裝或緩存在設備上,之後不管是否有網絡鏈接均可以加載它們。
對於這個應用,咱們將在安裝 Service Worker 時預先緩存全部靜態資源,以便把咱們運行應用所需的一切都存儲在用戶的設備上。爲了確保咱們的應用快速加載,咱們將使用緩存優先策略:不去網絡獲取資源,而是從本地緩存中取出;只有當緩存不可用時,咱們纔會嘗試從網絡中獲取它。
從本地緩存中取可消除任何網絡方面的變數。不管用戶使用何種網絡(WiFi,5G,3G 甚至 2G),咱們須要運行的關鍵資源都幾乎能夠當即使用。
Caution: 在此示例中,使用 cache-first
策略提供靜態資源,這會致使在不詢問網絡的狀況下返回任何緩存內容的副本。雖然 cache-first
策略易於實施,但它可能會在未來的演化中帶來挑戰。
stale-while-revalidate strategy 對某些類型的數據是理想的,好比本應用。它會盡量快地獲取屏幕上要顯示的數據,而後在網絡返回最新數據後進行更新。 Stale-while-revalidate 意味着咱們須要發起兩個異步請求,一個到緩存,一個到網絡。
在正常狀況下,緩存數據幾乎會當即返回,爲應用提供可使用的最新數據。而後,當網絡請求返回時,將使用來自網絡的最新數據更新應用。
對於咱們的應用,這提供了比 "網絡優先,回退到緩存" 策略更好的體驗,由於用戶沒必要等到網絡請求超時後纔在屏幕上看到某些內容。他們最初可能會看到較舊的數據,但一旦網絡請求返回,應用就將使用最新數據進行更新。
如前所述,應用須要啓動兩個異步請求,一個到緩存,一個到網絡。該應用使用 window
上的 caches
對象來訪問緩存並獲取最新數據。這是漸進加強的一個很好的例子,由於 caches
對象可能並不是在全部瀏覽器中均可用,若是不可用,網絡請求仍然能夠工做。
更新 getForecastFromCache()
函數,檢查 caches
對象是否在全局 window
對象中可用,若是是,請從緩存中請求數據。
// 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()
以獲取緩存的最新天氣預報:
// CODELAB: Add code to call getForecastFromCache.
getForecastFromCache(location.geo)
.then((forecast) => {
renderForecast(card, forecast);
});
咱們的天氣應用如今發出兩個異步數據請求,一個來自緩存,另外一個來自 fetch
。若是緩存中有數據,它將被很是快速地返回和渲染(幾十毫秒)。而後,當 fetch
響應時,將使用直接來自天氣 API 的最新數據更新卡片。
請注意緩存請求和 fetch
請求如何結束於更新天氣預報卡片的調用。應用要如何知道它是否顯示了最新的數據?這在 renderForecast()
的以下代碼中處理:
// If the data on the element is newer, skip the update.
if (lastUpdated >= data.currently.time) {
return;
}
每次更新卡片時,應用都會將數據的時間戳存儲在卡片上的隱藏屬性中。若是卡片上已存在的時間戳比傳遞給函數的數據新,應用就什麼也不作。
在 Service Worker 中,讓咱們添加一個 DATA_CACHE_NAME
以便咱們能夠將應用數據與應用外殼分開。更新應用外殼並清除舊緩存後,咱們的數據將保持不變,仍用於超快速加載。請記住,若是你的數據格式未來發生了變化,你就須要一種方法來處理這種狀況,並確保應用外殼和內容保持同步。
// 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
數組改成以下文件列表:
// 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
,由於本應用如今具備離線工做所需的全部必要資源,不會再顯示離線頁面。
Caution:在此示例中,咱們手動控制着本身的 Service Worker。每次咱們更新任何靜態資源時,咱們都須要從新刷新 Service Worker 並更新緩存,不然將提供舊內容。此外,當一個文件更改時,整個緩存都會無效並須要從新下載。這意味着即便修復一個簡單的單字符拼寫錯誤也將使緩存無效並要求再次下載全部內容 - 效率不夠高。 Workbox 能優雅地處理它,經過將其集成到你的構建過程當中,只有已更改的文件才須要更新,爲用戶節省了帶寬並讓你更輕鬆地進行維護!
爲了防止咱們 activate
事件不當心刪除數據,在 service-worker.js
的 activate
事件,把 if (key !== CACHE_NAME) {
改成:
if (key !== CACHE_NAME && key !== DATA_CACHE_NAME) {
咱們須要修改 Service Worker 以攔截對 weather API 的請求並將其響應存儲在緩存中,以便咱們之後能夠輕鬆地訪問它們。在 stale-while-revalidate 策略中,咱們但願把網絡響應做爲「真相之源」,始終由它向咱們提供最新信息。若是不能,也能夠失敗,由於咱們已經在應用中檢索到了最新的緩存數據。
更新 fetch
事件處理程序以便和其它對數據 API 的請求分開。
// 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
發出請求。一旦返回了響應,就打開緩存,克隆響應,將其存儲在緩存中,而後將響應返回給原始請求者。
咱們須要刪除 evt.request.mode !== 'navigate'
檢查,由於咱們但願這個 Service Worker 處理全部請求(包括圖像,腳本,CSS 文件等),而不只僅是導航。若是咱們留着這個檢查,則只會從 Service Worker 緩存中提供 HTML,其它全部內容都將從網絡請求。
該應用如今應該能徹底離線工做。刷新頁面以確保你已安裝最新的 Service Worker ,而後保存幾個城市並按應用上的刷新按鈕以獲取新的天氣數據。
而後轉到 DevTools 的 Application 面板上的 Cache Storage 窗格。展開該部分,你應該會在左側看到靜態緩存和數據緩存的名稱。打開數據緩存會顯示爲每一個城市存儲的數據。
而後,打開 DevTools 並切換到 Service Workers 窗格,選中 Offline 複選框,而後嘗試從新加載頁面,而後離線並從新加載頁面。
若是你是一個快速的網絡上,並但願看看天氣預報數據會如何在慢速鏈接上更新,請在 server.js
中把 FORECAST_DELAY
設置爲 5000
。這樣對天氣預報 API 的全部請求都將延遲 5000 毫秒。
再次運行 Lighthouse 是個好主意。
SEO 審計
*✅經過: 文檔已有元描述。
漸進式應用審計
*✅經過: 在離線時當前頁面未給出 200 響應。 *✅經過: 在離線時 start_url
未給出 200 響應。 *✅經過: 未註冊用來控制頁面和 start_url
的 Service Worker。 *✅經過: Web 應用清單符合可安裝性要求。 *✅經過: 已配置自定義閃屏。 *✅經過: 已設置地址欄的主題顏色。
安裝漸進式 Web 應用後,其外觀和行爲會與全部其它已安裝的應用相似。它與其它應用啓動時的位置相同。它在沒有地址欄或其它瀏覽器 UI 的應用中運行。與全部其它已安裝的應用同樣,它是任務切換器中的頂級應用。
在 Chrome 中,能夠經過其 "三點菜單" 來安裝漸進式 Web 應用,也能夠向用戶提供按鈕或其它 UI 組件,以提示他們安裝你的應用。
Success: 因爲 Chrome 的 "三點菜單" 的安裝體驗不夠顯眼,咱們建議你在應用中提供一些指示以通知用戶你的應用能夠安裝,並使用安裝按鈕來完成安裝過程。
爲了使用戶可以安裝漸進式 Web 應用,它須要知足一些條件 。最簡單的方法是使用 Lighthouse 來確保它符合可安裝的標準。
若是你使用此 codelab,你的 PWA 就已經符合這些標準了。
Key Point:對於本節,在 DevTools 的 Application 面板的 Service Workers 窗格中啓用 Bypass for network 複選框。選中後,請求將繞過 Service Worker 並直接發送到網絡。這簡化了咱們的開發過程,由於咱們在完成本節中的任務時沒必要更新咱們的 Service Worker 。
首先,讓咱們將 install.js
添加到 index.html
文件中。
<!-- CODELAB: Add the install script here -->
<script src="/scripts/install.js"></script>
beforeinstallprompt
事件若是符合添加到主屏幕條件 ,Chrome 將觸發 beforeinstallprompt
事件,你可使用該事件指示你的應用能夠「安裝」,而後提示用戶安裝它。添加以下代碼以監聽 beforeinstallprompt
事件:
// CODELAB: Add event listener for beforeinstallprompt event
window.addEventListener('beforeinstallprompt', saveBeforeInstallPromptEvent);
在咱們的 saveBeforeInstallPromptEvent
函數中,咱們將保存對 beforeinstallprompt
事件的引用,以便咱們稍後能夠調用它的 prompt()
,並修改咱們的 UI 以顯示安裝按鈕。
// CODELAB: Add code to save event & show the install button.
deferredInstallPrompt = evt;
installButton.removeAttribute('hidden');
當用戶單擊安裝按鈕時,咱們須要調用保存的 beforeinstallprompt
事件的 .prompt()
函數。咱們還須要隱藏安裝按鈕,由於 .prompt()
只能在每一個保存的事件上調用一次。
// 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
屬性返回的 Promise 來檢查用戶是如何響應的安裝對話框。在顯示出提示而且用戶已對其做出響應後,Promise 將返回一個具備 outcome
屬性的對象。
// 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
的一個說明, 規範中把它定義成了屬性 ,而不是你所指望的函數。
除了你所添加的用於安裝應用的任何 UI 以外,用戶還能夠經過其它方法安裝 PWA,例如 Chrome 的 "三點菜單"。要跟蹤這些事件,請監聽 appinstalled 事件。
// CODELAB: Add event listener for appinstalled event
window.addEventListener('appinstalled', logAppInstalled);
而後,咱們須要修改 logAppInstalled
函數,對於這個 codelab,咱們只用了 console.log
,但在生產應用中,你可能但願將其做爲事件記錄在你的分析軟件中。
// CODELAB: Add code to log the event
console.log('Weather App was installed.', evt);
不要忘記修改 service-worker.js
文件中的 CACHE_NAME
,由於你對已緩存的文件作了些更改。在 DevTools 的 「Application」 面板上的 「Service Worker」 窗格中啓用 Bypass for network 複選框只在開發中有用,在現實世界中是無濟於事的。
讓咱們看看咱們的安裝步驟是如何進行的。爲了安全起見,使用 DevTools 應用面板中的 Clear site storage 按鈕清除全部內容以確保咱們從頭開始。若是你以前安裝過該應用,請務必將其卸載,不然安裝圖標將不會再次顯示。
首先,驗證咱們的安裝圖標是否正確顯示了,請務必同時在桌面和移動設備上試用。
接下來,讓咱們確保一切都安裝正確,並正確觸發了咱們的事件。你能夠在桌面設備或移動設備上執行此操做。若是你想在移動設備上進行測試,請確保使用遠程調試,以便查看控制檯中的日誌。
請注意,若是你從 localhost 在桌面上運行,則你安裝的 PWA 可能會顯示地址標題,由於 localhost 不認爲是安全主機。
咱們還要檢查它在 iOS 上的行爲。若是你有 iOS 設備,能夠直接使用它;或者若是你使用的是 Mac,能夠嘗試使用 Xcode 提供的 iOS 模擬器。
媒體查詢 display-mode
能夠根據應用的啓動方式來應用樣式,或者使用 JavaScript 來斷定它是如何啓動的。
@media all and (display-mode: standalone) {
body {
background-color: yellow;
}
}
你還能夠在 JavaScript 中檢測它是否運行在獨立模式下來檢查這個 display-mode
媒體查詢 。
請記住,若是已經安裝了應用,則 beforeinstallevent
不會觸發,所以在開發期間,你可能須要屢次安裝和卸載應用,以確保一切正常運行。
在 Android 上,卸載 PWA 的方式與卸載其它已安裝的應用的方式相同。
在 ChromeOS 上,能夠從啓動器搜索框輕鬆卸載 PWA。
在 Mac 和 Windows 上,必須經過 Chrome 卸載 PWA。
恭喜,你已經成功構建了第一個漸進式 Web 應用!
你添加了一個 Web 應用清單以使其可以安裝,並添加了一個 Service Worker 以確保你的 PWA 始終快速並且可靠。你學習瞭如何使用 DevTools 審計應用以及如何用它幫你改善用戶體驗。
你如今知道了將任何 Web 應用轉換爲漸進式 Web 應用所需的關鍵步驟。