如何優化漸進式 Web 應用程序:超越基礎

已發表: 2022-03-10
快速總結 ↬漸進式 Web 應用程序被證明可以提高用戶參與度並有效降低成本。 構建現代 PWA 需要的不僅僅是核心設置來滿足用戶的期望。 因此,讓我們親眼看看如何為 PWA 添加當代特性,從離線運行到用戶友好的權限請求。

漸進式 Web 應用程序 (PWA) 在 2020 年仍然越來越受歡迎。考慮到更高的轉化率、客戶參與度、頁面加載速度降低以及開發和管理成本降低等好處,這並不令人意外。

我們可以看到受人尊敬的公司也通過他們的 PWA 獲得成功,例如 Twitter、Uber、Tinder、Pinterest 和 Forbes。 他們都吹噓實施漸進式應用程序帶來的巨大好處。

好消息是,開發 PWA 並不是只有大預算公司才能負擔得起的。 這些應用程序平等地服務於小型和普通企業,並且創建起來並不復雜。

您可以在 Smashing Magazine 上找到全面的漸進式 Web 應用初學者指南,該指南側重於構建 PWA 的核心。

但是,讓我們更進一步,學習如何將現代品質部署到 PWA 中,例如離線功能、基於網絡的優化、跨設備用戶體驗、SEO 功能以及非侵入式通知和請求。 您還將找到示例代碼或對更具體指南的引用,以便您可以在 PWA 中實施這些技巧。

漸進式 Web 應用程序 (PWA) 快速概覽

讓我們不要跳過基礎知識,快速了解 PWA 的核心。

什麼是 PWA?

“Progressive Web Apps (PWA) 使用現代 API 構建和增強,以提供增強的功能、可靠性和可安裝性,同時通過單一代碼庫在任何設備上覆蓋任何人、任何地方。”

——谷歌的開發者

換句話說,PWA 是用戶可以用作獨立應用程序的網站。 它們與原生應用的不同主要是因為 PWA 不需要安裝並且可以與各種設備一起使用——原生應用主要是為移動設備構建的。

PWA 是如何工作的?

PWA 的核心由三個組件組成:Web 應用程序清單、服務工作者和應用程序外殼。 您可以在上面提到的初學者指南中找到構建這些的詳細說明。

以下是這些組件的作用。

網絡應用清單

Web 應用程序清單是使網站以全屏模式作為獨立應用程序運行的核心。 您可以定義 PWA 的外觀,針對不同的設備對其進行優化,並分配一個在應用程序安裝後顯示的圖標。

服務人員

服務工作者通過獲取緩存數據或通知用戶沒有 Internet 連接來啟用 PWA 的離線使用。 一旦服務器連接恢復,服務工作者也會檢索最新數據。

應用程序外殼架構

應用程序外殼是用戶在訪問 PWA 時看到的內容。 它是支持用戶界面所需的最低 HTML、CSS 和 JavaScript。 在開發 PWA 時,您可以在瀏覽器中緩存應用程序外殼的資源和資產。

跳躍後更多! 繼續往下看↓

將現代特徵部署到您的 PWA

除了核心功能之外,現代 PWA 還包含其他特徵,這些特徵進一步推動其用戶獲得更非凡的用戶體驗。

讓我們看看 PWA 的一些特定現代特徵,並了解如何將這些添加到您的 PWA 中。 以下品質被 Google 開發人員認為是對基本 PWA 的重要補充。

該應用程序可以像在線一樣離線工作

在構建 PWA 時,您還可以開發自定義離線頁面作為核心的一部分。 但是,如果您的 PWA 即使在沒有 Internet 連接的情況下也能繼續運行(直到需要連接的某個點),它對用戶來說會更加友好。 否則,用戶體驗可能會像 Ankita Masand 在她關於 PWA 的痛點的文章中所描述的那樣,在訂購蛋糕時所遭受的折磨一樣令人沮喪。

您可以通過使用緩存內容、後台同步和骨架屏幕來獲得更重要的用戶體驗。 讓我們來看看每一個。

使用IndexedDB緩存內容

IndexedDB是一個瀏覽器內的 NoSQL 存儲系統,您可以使用它來緩存和檢索所需的數據,以使您的 PWA 脫機工作。

但是,並非所有瀏覽器都支持IndexedDB ,因此您要做的第一件事是檢查用戶的瀏覽器是否支持它。

 if (!('indexedDB' in window)) { console.log('This browser doesn\'t support IndexedDB'); return; }

在此之後,您可以使用 IndexedDB API 創建緩存內容。 下面是一個來自 Google 開發人員的示例,它打開一個數據庫、添加一個對象存儲以及向該存儲中添加一個項目。

 var db; var openRequest = indexedDB.open('test_db', 1); openRequest.onupgradeneeded = function(e) { var db = e.target.result; console.log('running onupgradeneeded'); if (!db.objectStoreNames.contains('store')) { var storeOS = db.createObjectStore('store', {keyPath: 'name'}); } }; openRequest.onsuccess = function(e) { console.log('running onsuccess'); db = e.target.result; addItem(); }; openRequest.onerror = function(e) { console.log('onerror!'); console.dir(e); }; function addItem() { var transaction = db.transaction(['store'], 'readwrite'); var store = transaction.objectStore('store'); var item = { name: 'banana', price: '$2.99', description: 'It is a purple banana!', created: new Date().getTime() }; var request = store.add(item); request.onerror = function(e) { console.log('Error', e.target.error.name); }; request.onsuccess = function(e) { console.log('Woot! Did it'); }; }

後台同步

如果您的 PWA 在後台同步數據,則用戶可以在離線時執行操作,然後在 Internet 連接恢復時執行這些操作。 一個簡單的例子是一個消息應用程序。 用戶可以在離線時發送消息,而無需等待消息發送——後台同步會在連接恢復時自動發送消息。

這是 Jake Archibald 如何開發後台同步功能的示例。

 // Register your service worker: navigator.serviceWorker.register('/sw.js'); // Then later, request a one-off sync: navigator.serviceWorker.ready.then(function(swRegistration) { return swRegistration.sync.register('myFirstSync'); });

然後在/sw.js中監聽事件:

 self.addEventListener('sync', function(event) { if (event.tag == 'myFirstSync') { event.waitUntil(doSomeStuff()); } });

骨架屏風

使用骨架屏幕的主要好處之一是用戶感知應用程序正在工作而不是閒置。 雖然用戶沒有連接,但骨架屏幕會在沒有內容的情況下繪製界面 - 然後在連接恢復後填充。

Code My UI 提供了一些出色的代碼片段,可用於為 PWA 創建骨架屏幕。

Code My UI 上的骨架屏幕示例
Code My UI 上的骨架屏幕示例。 (大預覽)

根據網絡使用情況進行優化

PWA 的一個核心好處是它為用戶提供了更快的體驗。 您可以通過讓 PWA 使用緩存優先網絡、優先資源以及使用基於網絡質量的自適應加載來進一步優化加載速度。

讓我們看看如何將這些開發到您的 PWA 中。

先緩存,後網絡

首先使用緩存的內容進一步使您的 PWA 能夠離線運行,並為用戶即使在低網絡覆蓋區域也可以訪問內容鋪平了道路。 您可以通過創建一個服務工作者來緩存內容然後獲取它來做到這一點。

這是 Jeff Posnick 關於使用服務工作者緩存靜態 HTML 的示例。

 self.addEventListener('fetch', event => { if (event.request.mode === 'navigate') { // See /web/fundamentals/getting-started/primers/async-functions // for an async/await primer. event.respondWith(async function() { // Optional: Normalize the incoming URL by removing query parameters. // Instead of https://example.com/page?key=value, // use https://example.com/page when reading and writing to the cache. // For static HTML documents, it's unlikely your query parameters will // affect the HTML returned. But if you do use query parameters that // uniquely determine your HTML, modify this code to retain them. const normalizedUrl = new URL(event.request.url); normalizedUrl.search = ''; // Create promises for both the network response, // and a copy of the response that can be used in the cache. const fetchResponseP = fetch(normalizedUrl); const fetchResponseCloneP = fetchResponseP.then(r => r.clone()); // event.waitUntil() ensures that the service worker is kept alive // long enough to complete the cache update. event.waitUntil(async function() { const cache = await caches.open('my-cache-name'); await cache.put(normalizedUrl, await fetchResponseCloneP); }()); // Prefer the cached response, falling back to the fetch response. return (await caches.match(normalizedUrl)) || fetchResponseP; }()); } });

優先考慮資源

默認情況下,由於 PWA 的精簡特性,其性能優於類似的原生應用程序。 此外,由於 PWA 使用瀏覽器的緩存,因此可以指示哪些資源具有優先級,甚至在使用之前就需要渲染。 這主要適用於靜態元素,因為動態內容需要在獲取之前更新。

您可以使用 HTML 中的<link>字符串指定元素的優先級。 您還可以使用rel=”preconnect”rel=”dns-prefetch.”

Maximiliano Firtman 通過在瀏覽器引擎中優先考慮 Web 字體給出了一個簡單的例子:

 <link rel=”preload” as=”font” href=”font.woff” crossorigin>

實現自適應加載

WiFi 和 4G 的網速並非無處不在,用戶仍然可以通過 2G 和 3G 連接上網。 由於您希望盡可能多的人可以訪問您的 PWA,因此您可能希望優化它以在較低的 Internet 速度下執行。

您可以通過實現自適應加載來實現這一點,它根據用戶擁有的連接類型加載 PWA 元素。

谷歌的自適應加載插圖。
谷歌的自適應加載插圖。 (大預覽)

最直接的方法是使用 Google 的 Workbox 工具,其中包括許多現成的緩存策略插件。

假設您要定義自定義緩存策略。 以下是 Demian Renzulli 和 Jeff Posnick 的示例:

 const adaptiveLoadingPlugin = { requestWillFetch: async ({request}) => { const urlParts = request.url.split('/'); let imageQuality; switch ( navigator && navigator.connection ? navigator.connection.effectiveType : '' ) { //... case '3g': imageQuality = 'q_30'; break; //... } const newUrl = urlParts .splice(urlParts.length - 1, 0, imageQuality) .join('/') .replace('.jpg', '.png'); const newRequest = new Request(newUrl.href, {headers: request.headers}); return newRequest; }, };

接下來,將插件傳遞給包含正則表達式的cacheFirst策略以匹配圖像 URL(例如/img/ ):

 workbox.routing.registerRoute( new RegExp('/img/'), workbox.strategies.cacheFirst({ cacheName: 'images', plugins: [ adaptiveLoadingPlugin, workbox.expiration.Plugin({ maxEntries: 50, purgeOnQuotaError: true, }), ], }), );

所有平台上的出色用戶體驗

出色的 PWA 可以在瀏覽器、移動設備和平板電腦上無縫運行。 雖然使用 Android 設備是訪問 Internet 的最流行方式(佔 38.9% 的市場份額),但針對所有平台優化您的應用程序是開發 PWA 核心功能的一部分。

您可以採取進一步措施來提高可用性並提供出色的用戶體驗,例如減少 PWA 加載時的跳躍,並確保您的 PWA 可與任何輸入法一起使用。

以下是您如何處理這些方面的每一個方面。

減少“跳躍”的內容加載

即使使用高速互聯網,網站的內容在加載時也會發生變化,因為網站的元素是按順序加載的。 如果連接速度較慢,這種效果會更糟,嚴重損害用戶體驗。

加載時導致內容移動的最常見元素是圖像,因為這些元素通常較大並且在加載內容時不是優先級。 您可以通過使用較小的佔位符圖像來解決“延遲加載”這個問題,您可以在呈現 HTML 結構後優先處理這些佔位符圖像。

下面是 Mozilla 開發人員的一個示例,說明如何在 JavaScript 中添加在實際圖像之前首先加載的輕量級圖像:

 <img src='data/img/placeholder.png' data-src='data/img/SLUG.jpg' alt='NAME'>

app.js文件處理 data-src 屬性,如下所示:

 let imagesToLoad = document.querySelectorAll('img[data-src]'); const loadImages = (image) => { image.setAttribute('src', image.getAttribute('data-src')); image.onload = () => { image.removeAttribute('data-src'); }; };

然後創建一個循環:

 imagesToLoad.forEach((img) => { loadImages(img); });

您還可以查看 Smashing Magazine 上關於減少與其他元素的內容跳轉的詳盡指南。

PWA 適用於任何輸入法

我們已經介紹了 PWA 應該如何與各種不同的設備一起工作。 為了更進一步,您還需要考慮用戶可以在這些設備上使用的其他輸入方法,例如觸摸、鼠標和触控筆。

將指針事件 API 添加到您的 PWA 主要解決了這個問題。 以下是根據 Google 開發人員介紹的方法。

首先,檢查瀏覽器是否支持指針事件:

 if (window.PointerEvent) { // Yay, we can use pointer events! } else { // Back to mouse and touch events, I guess. }

接下來,您可以定義各種輸入法可以執行的操作:

 switch(ev.pointerType) { case 'mouse': // Do nothing. break; case 'touch': // Allow drag gesture. break; case 'pen': // Also allow drag gesture. break; default: // Getting an empty string means the browser doesn't know // what device type it is. Let's assume mouse and do nothing. break; }

由於大多數瀏覽器已經具備觸控功能,因此您無需添加任何其他內容。

可通過搜索發現

PWA 與原生應用程序相比的主要優勢之一是 PWA 本質上是一個網站,搜索引擎可以索引它們。 這使您可以部署 SEO 策略,使您的 PWA 內容更容易被發現。

您可以首先確保 PWA 中的每個 URL 都具有唯一的描述性標題和元描述,這是任何 SEO 優化活動的基準。

讓我們看看您可以採取哪些其他步驟來使您的 PWA 可搜索。

分析 PWA 的可查找性

Google 在其 Search Console 中有一個出色的工具,可以分析您的網站 (PWA) 並報告結果。 您可以使用它對您的網站進行基本掃描,並發現任何可以開始修復的弱點。

或者,您可以在 Chrome 瀏覽器中使用 Lighthouse 來運行 SEO 審核。

首先,導航到目標 URL。 然後按Control+Shift+J (或 Mac 上的Command+Option+J )打開開發人員的工具菜單。 選擇 Lighthouse 選項卡,勾選 SEO 類別框,然後生成報告。

谷歌 Chrome 瀏覽器 Lighthouse 開發者工具類 SEO 截圖。
谷歌 Chrome 瀏覽器 Lighthouse 開發者工具類 SEO 截圖。 (大預覽)

使用結構化數據

Google 的搜索引擎使用結構化數據來了解您網頁上內容的用途。

結構化數據是一種標準化格式,用於提供有關頁面的信息並對頁面內容進行分類; 例如,在食譜頁面上,成分是什麼,烹飪時間和溫度,卡路里等等。
- 谷歌

在您開始編碼之前,Google 還整理了一份有用的常見結構化數據錯誤列表以及修復這些錯誤的相關指南。 學習本材料應該為您提供一個很好的基線,以了解應避免的情況。

Frederick O'Brien 在 Smashing Magazine 上寫了一篇出色的指南,將結構化數據烘焙到設計過程中,其中描述瞭如何從一開始就構建結構化數據。

用戶友好的通知和權限請求

最後但並非最不重要的一點是,您可以通過優化通知和權限請求來增加用戶體驗,以便它們為您的用戶服務——而不僅僅是令人困惑和煩人。

雖然您通常可以依靠常識,但您也可以實施一些實用技巧,例如創建非侵入式推送通知並為用戶提供取消訂閱消息的選項。

微妙的通信許可請求

有兩種現代方式可以自動化網站和用戶之間的通信——聊天機器人和通知。

在 PWAs 上下文中,聊天機器人的主要優點是它不需要用戶的許可來與用戶交互。 但是,根據您使用的聊天機器人應用程序,用戶可能會錯過微妙的消息。 另一方面,通知需要用戶的許可,但更明顯。

由於您可以將聊天機器人添加為單獨的第三方應用程序,因此讓我們專注於創建用戶友好的推送通知。 如果您首先需要有關如何創建推送通知的指南,這裡是 Indrek Lasn 的一個很棒的指南。

創建非侵入式權限請求的最直接方法是使用雙重請求。 這意味著您在用戶操作系統的默認交互之上包含與站點的自定義交互。

Matt Gaunt 通過以下圖片為這種效果提供了完美的插圖。

這是不提供上下文的默認通知權限請求:

錯誤的 UX 權限請求示例
Permission UX 的錯誤 UX 權限請求示例。 (大預覽)

這是在上述默認通知權限之前添加的自定義交互:

良好的 UX 權限請求示例
Permission UX 提供的良好 UX 權限請求示例。 (大預覽)

通過在操作系統的默認警報之前添加自定義警報,您可以更清楚地向用戶描述通知的目的。 這增加了用戶選擇接收您網站通知的機會。

允許用戶選擇退出通知

對於用戶來說,無論他們使用什麼設備,禁用站點的推送通知都是非常煩人的。 因此,就用戶體驗而言,為用戶提供選擇退出消息的選項大有幫助。

下面是一個來自 Matt Gaunt 的例子,關於如何在你的代碼和 UI 中實現取消訂閱功能:

首先,定義pushButton的點擊監聽器:

 pushButton.addEventListener('click', function() { pushButton.disabled = true; if (isSubscribed) { unsubscribeUser(); } else { subscribeUser(); } });

然後,添加一個新函數:

 function unsubscribeUser() { swRegistration.pushManager.getSubscription() .then(function(subscription) { if (subscription) { // TODO: Tell application server to delete subscription return subscription.unsubscribe(); } }) .catch(function(error) { console.log('Error unsubscribing', error); }) .then(function() { updateSubscriptionOnServer(null); console.log('User is unsubscribed.'); isSubscribed = false; updateBtn(); }); }

這是成功實現訂閱按鈕以在用戶選擇時禁用通知後它在控制台中的外觀。

Console.log 成功啟用/禁用通知功能的示例
由 Matt Gaunt 成功啟用/禁用通知功能的Console.log示例。 (大預覽)

結論

PWA 在增加網站流量和擴展用戶體驗方面非常強大。 要進一步優化您的 PWA,您可以使用本文中描述的這些現代解決方案來增強性能和功能。

您也不需要一次實現所有內容。 隨意挑選最相關的選項,因為這些建議中的每一個都可以幫助您進一步推進 PWA。

更多資源

  • Google 的漸進式 Web 應用程序培訓
  • web.dev 的漸進式 Web 應用程序
  • Mozilla 的漸進式 Web 應用程序 (PWA)