[Google I/O2021]優化PWA體驗的四個建議

本文內容來自視頻直播,若是想要了解更多內容能夠點擊每一段落的標題打開對應的博文進行了解
想要了解PWA以及相應的優化技巧,推薦閱讀一下 PWA應用實戰 ,雖然裏面有部份內容已通過氣+過時了,不過裏面的思想仍是能夠活用於如今的javascript

爲了讓網頁能夠提供原生應用般的體驗,PWA自提出以來愈來愈受歡迎。單就本身的使用體驗而言,愈來愈多的谷歌網頁都開始適配PWA支持,使得之前要在標籤頁裏輸入打開的網頁成爲一個能夠直接在開始菜單啓動的應用。除了一般的適配策略以外,在這裏分享五個用於提供更好用戶體驗的PWA優化技巧。html

提供離線時的fallback頁面

谷歌相冊會在網絡很差的時候跳轉到斷網頁面 serviceWorker能夠對於網絡請求進行緩存,這樣不但能夠提升加載的速度,還能夠在網絡請求失敗的時候(例如用戶網絡環境很差)進行兜底,用緩存數據替代從網絡獲取失敗的頁面(並不推薦使用跳轉的方式,由於這樣意味着用戶可能會丟失想要訪問的頁面URL)。所以,對於不少須要大量網絡交互的應用能夠在用戶進入頁面的時候經過一個友好的頁面告知用戶應用在網路狀態很差的狀況下不能使用。對於一些純前端的小應用就不必增長離線告知界面了,只須要緩存應用代碼讓用戶離線使用便可。前端

思路首次加載的時候註冊SW,緩存OFFLINE.html->下次訪問的頁面若是既沒有匹配到SW的緩存又沒有成功經過網絡fetch到,就返回以前混存了的OFFLINE.html告知用戶此次打開的時候網絡有問題->在離線界面不斷地循環fetch操做,若是可以成功從網絡拉取到任何信息則說明網絡恢復,自動跳轉到應用界面java

代碼片斷

首先,準備一個告知用戶當前離線的頁面。爲了便於緩存以及節省用戶的存儲空間,樣式和腳本最好內嵌在同一個文件中。
其中,腳本方面,須要對於兩部分進行監聽:首先,固然是瀏覽器的網絡訪問情況,所以,監聽Navigator.online事件,網絡恢復就從新加載git

window.addEventListener('online', () => {
        window.location.reload();
      });  
複製代碼

除此以外,還有可能服務器出現問題致使不能訪問,所以對於這種用戶在線服務器當機的狀況,設定定時循環操做按期檢驗服務器狀態,直到服務器有響應再從新加載github

async function checkNetworkAndReload() {
        try {
          const response = await fetch('.');
          // 檢驗是否能從服務器得到應答
          if (response.status >= 200 && response.status < 500) {
            window.location.reload();
            return;
          }
        } catch {
          //用戶在線,服務器不能正常工做,那也不用從新加載頁面了
        }
        //做爲按期任務
        window.setTimeout(checkNetworkAndReload, 2500);
      }

      checkNetworkAndReload();
複製代碼

頁面準備好了就要配置SW讓其能夠在斷網狀況下加載出來,請參考註釋內容web

/* Copyright 2015, 2019, 2020, 2021 Google LLC. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */

// Incrementing OFFLINE_VERSION will kick off the install event and force
// previously cached resources to be updated from the network.
// This variable is intentionally declared and unused.
// Add a comment for your linter if you want:
// eslint-disable-next-line no-unused-vars
const OFFLINE_VERSION = 1;
const CACHE_NAME = "offline";
// Customize this with a different URL if needed.
const OFFLINE_URL = "offline.html";

self.addEventListener("install", (event) => {
  event.waitUntil(
    (async () => {
      const cache = await caches.open(CACHE_NAME);//若是SW成功安裝,就在對應的cache版本中保存離線用頁面
      // Setting {cache: 'reload'} in the new request will ensure that the
      // response isn't fulfilled from the HTTP cache; i.e., it will be from
      // the network.
      await cache.add(new Request(OFFLINE_URL, { cache: "reload" }));
    })()
  );
  // Force the waiting service worker to become the active service worker.
  self.skipWaiting();
});

self.addEventListener("activate", (event) => {
  event.waitUntil(
    (async () => {
      // Enable navigation preload if it's supported.
      // See https://developers.google.com/web/updates/2017/02/navigation-preload
      if ("navigationPreload" in self.registration) {
        await self.registration.navigationPreload.enable();
      }
    })()
  );

  // Tell the active service worker to take control of the page immediately.
  self.clients.claim();
});

self.addEventListener("fetch", (event) => {
  // We only want to call event.respondWith() if this is a navigation request
  // for an HTML page.
  if (event.request.mode === "navigate") {//只有跳轉請求才使用一整個離線頁面接管
    event.respondWith(
      (async () => {
        try {
          // 首先看看瀏覽器的preloadResponse能不能返回
          const preloadResponse = await event.preloadResponse;
          if (preloadResponse) {
            return preloadResponse;
          }

          // 而後使用網絡請求
          const networkResponse = await fetch(event.request);
          return networkResponse;
        } catch (error) {
        //若是出現錯誤就用以前緩存的離線頁面進行迴應
          // catch is only triggered if an exception is thrown, which is likely
          // due to a network error.
          // If fetch() returns a valid HTTP response with a response code in
          // the 4xx or 5xx range, the catch() will NOT be called.
          console.log("Fetch failed; returning offline page instead.", error);

          const cache = await caches.open(CACHE_NAME);
          const cachedResponse = await cache.match(OFFLINE_URL);
          return cachedResponse;
        }
      })()
    );
  }

  // If our if() condition is false, then this fetch handler won't intercept the
  // request. If there are any other fetch handlers registered, they will get a
  // chance to call event.respondWith(). If no fetch handlers call
  // event.respondWith(), the request will be handled by the browser as if there
  // were no service worker involvement.
});
複製代碼

按照谷歌相冊的使用流程,當網絡與服務器的鏈接不穩定的時候,會顯示「相冊只能在網絡下使用」的提示,中間有一個「重試」的按鈕讓用戶能夠主動從新加載,即使瀏覽器處於online狀態,可是不能訪問谷歌的時候也不會進行跳轉,當網絡能夠訪問的時候,便自動刷新頁面。
能夠在開發者工具-Application-SW設置裏開啓offline模式,查看谷歌家的PWA應用是如何處理網絡問題的。
不過有的應用則是選擇優先加載應用骨架,而後經過組件的提示來告知斷網狀況,各有利弊吧express

image.png

給應用增長快捷指令

image.png
許多應用都會有些經常使用功能,例如推特常常須要查看我的首頁,或者發佈消息,對於筆記應用,對於收藏夾增長個快捷方式也是很不錯的。所以,咱們能夠在PWA的聲明文件中聲明一些經常使用連接/功能的快捷方式,這樣,對於移動端用戶,能夠經過在桌面長按安裝了的PWA的圖標選擇快捷方式,桌面端用戶也能夠右鍵單擊圖標的方式進行使用。apache

代碼示例

要讓一個PWA能夠被安裝,同時讓用戶和瀏覽器更加理解應用提供的功能,manifest必不可少,所以,若是須要添加快捷方式,也只須要在shortcuts項中聲明便可。json

{
  "name": "Player FM",
  "start_url": "https://player.fm?utm_source=homescreen",
  …
  "shortcuts": [
    {
      "name": "Open Play Later",
      "short_name": "Play Later",
      "description": "View the list of podcasts you saved for later",
      "url": "/play-later?utm_source=homescreen",
      "icons": [{ "src": "/icons/play-later.png", "sizes": "192x192" }]
    },
    {
      "name": "View Subscriptions",
      "short_name": "Subscriptions",
      "description": "View the list of podcasts you listen to",
      "url": "/subscriptions?utm_source=homescreen",
      "icons": [{ "src": "/icons/subscriptions.png", "sizes": "192x192" }]
    }
  ]
}
複製代碼

其中,只有 name,url是必要的。全部被聲明瞭的快捷方式能夠在開發者工具中查看,使用的時候須要注意:把最經常使用的放在shortcuts數組的最前面,名字須要簡潔直接 image.png

作好應用聲明的國際化

雖然感受這個不屬於前端的範疇了,不過仍是值得講一下。以前提到了PWA經過聲明文件告知瀏覽器和用戶本身的各類信息和功能,所以,對於不一樣語言的用戶,提供不一樣語言的聲明文件十分有必要,這樣一樣是添加一個應用在桌面,中文用戶顯示的是推特,英文用戶顯示的就是twitter了。其實現有兩種方式。 許多人會把應用託管在GHPages上,所以須要把更換manifest的邏輯放在前端,所以能夠參照這篇文章進行操做,具體思路就是判斷navigator.language而後根據得到的用戶語言動態生成對應語言的manifest節點。

<script>
    var userLan = navigator.language || 'en-US'
link = document.createElement('link');
    link.href = './manifest/manifest.' + userLan + '.json';
    link.rel = 'manifest';
document.getElementsByTagName('head')[0].appendChild(link);
</script>
<!-- <link rel="manifest" href="/manifest/manifest.zh-CN.json" > --> 複製代碼

不過這個有一個缺點,由於應用安裝事後不能更改manifest的路徑/文件名,不然就不能對其進行更新。所以若是用戶中間更改了須要的語言,就須要卸載從新安裝了。
所以,能夠對服務器進行操做,推薦使用請求頭來判斷用戶語言,而後給用戶的同一請求返回對應語言的文件的方式,這樣manifest的路徑不變,PWA就能夠以更新的方式切換應用聲明的語言了。能夠經過獲取請求頭的accept-language來決定語言,若是應用提供設置界面給用戶切換應用語言的話(推特,又是你),還能夠先讀取cookie攜帶的信息,經過用戶設置的語言->瀏覽器語言的方式決定返回什麼語言的聲明。

監測用戶使用PWA功能的效果

一樣是網頁應用,其又能夠直接在瀏覽器直接瀏覽,也能夠經過添加到桌面的方式像一個獨立的應用進行使用。那確定是但願用戶可以安裝一下啦(,因此就搞個推薦窗口引導用戶安裝,而且進行統計吧。

引導用戶安裝

若是瀏覽器判斷用戶在一個頁面中使用的活躍度比較高的話,就會自動彈出以下的窗口建議用戶安裝PWA應用到桌面以更好地使用。不過不少用戶不明白這是什麼,因此就讓咱們本身來接管吧(使命感) image.png

let defferedPromt;//這個存儲被接管了的瀏覽器PWA安裝推廣事件
window.addEventListener('beforeinstallpromt',e=>{
e.preventDefault();//接管
defferedPromt =e;//用於用戶點擊了咱們提供的按鈕以後的調用
showCustomPWAPromtInWeb();
ga(xxxx);
})
複製代碼

showCustomPWAPromtInWeb函數中,咱們須要在本身的頁面中彈出推廣窗口,推薦用戶將應用進行安裝,威逼利誘下啥的,例如「我這裏有好康的,讓我安裝!」或者"咱們發現您在咱們頁面中使用了不少次,是否有興趣將其安裝到桌面,這樣就能夠以獨立程序的形式進行使用啦"這樣的,用戶點擊了yes以後,就調用 defferedPromt.promt() 這時候瀏覽器纔會彈出以前的那個安裝確認氣泡,這樣用戶的安裝機率就大大提高了。

作好用戶使用的統計

小應用什麼的隨便啦,不過相信對於適配了PWA的開發者而言,仍是對於本身的PWA效果挺好奇的吧,用戶喜不喜歡,適配PWA能不能提高用戶留存啥的。所以能夠對於如下的流程進行埋點統計:
首先,當彈出了PWA安裝提示的時候,以及用戶對於瀏覽器安裝PWA提示的選擇 await defferedPromt.userChoice 能夠獲得用戶對於PWA的接受程度,同時還能夠監聽appInstalled事件,判斷用戶是自行點擊瀏覽器地址欄的應用安裝的仍是5經過咱們彈出的小廣告來安裝的。同時,還能夠匹配當前應用是在standalone(獨立應用窗口)仍是tab的形式進行使用,來生成對應的用戶畫像。

閒話

我很喜歡PWA!由於可讓一個簡單寫出來的網頁有應用般的效果! 如今瀏覽器愈來愈厲害了,讓web應用有原生應用般的體驗,不管是網絡優化,仍是系統支持,都在愈來愈完善,我也發現不少我平時使用的平臺開始對於PWA進行支持了。不過國內的網絡走向了另外一個方向,首先是閹割web功能,強制下載app製造圍城,齊次國內的瀏覽器也閹割了對於web標準的支持,不過別人怎麼樣是別人的事,若是本身在寫web小工具的時候能夠進行優化,相信別人在使用的時候也能會心一笑吧xd

相關文章
相關標籤/搜索