在專案中使用Service Worker 與 PWA

發表於2023-09-28

在這裡插入圖片描述

小冊

這是我整理的學習資料,非常系統和完善,歡迎一起學習

引言

最近next專案有使用pwa技術,使用起來也不復雜,目前瀏覽器的相容性也比較良好

Service Worker是瀏覽器中獨立於網頁執行的指令碼,而PWA(漸進式Web應用程式)是一種Web應用程式,其外觀和感覺類似於原生應用程式。在討論Service Worker與PWA之前,讓我們先簡要了解一下Web Worker。

Web Worker

1. 什麼是 Web Worker?

Web Worker 是瀏覽器內建的執行緒,用於執行非阻塞事件迴圈的 JavaScript 程式碼。由於 JavaScript 是單執行緒語言,一次只能處理一個任務。複雜任務的出現可能導致主執行緒被阻塞,嚴重影響使用者體驗。Web Worker 的作用是允許主執行緒建立 worker 執行緒,使它們可以同時執行。Worker 執行緒主要負責處理複雜的計算任務,然後將結果返回給主執行緒。簡而言之,worker 執行緒執行復雜計算,同時保持頁面(主執行緒)的流暢性,不會造成阻塞。

2. 型別

Web Worker 有三種主要型別:

  1. Dedicated Workers【專用 Worker】由主執行緒例項化,只能與主執行緒通訊。
  2. Shared Workers【共享 Worker】可以被同源的所有執行緒訪問。
  3. Service Workers【服務 Worker】能夠控制其關聯的網頁,攔截和修改導航、資源請求,並快取資源,使您能夠在某些情況下靈活控制應用程式的行為。

3. 限制

1. 同源限制

分配給 Worker 執行緒執行的指令碼檔案必須與主執行緒的指令碼檔案同源,通常都應該放在同一專案下。

2. DOM 限制

Web Workers 無法訪問某些關鍵的 JavaScript 特性,包括:

1 DOM(因為這可能導致執行緒不安全)

2 window 物件

3 document 物件

4 parent 物件

3. 檔案限制

出於安全考慮,worker 執行緒無法讀取本地檔案。它們載入的指令碼必須來自網路,並且必須與主執行緒的指令碼同源。

什是Service Worker?

Service Worker(服務工作執行緒)是一種在瀏覽器背後執行的指令碼,用於提供強大的離線和快取功能,以改善 Web 應用程式的效能和可靠性。它是漸進式網路應用程式(Progressive Web App,PWA)的關鍵組成部分,可以讓 Web 應用程式更像本地應用程式,即使在離線狀態下也能正常工作。Service Worker 是 Web 開發中的一個強大工具,它使開發人員能夠更好地控制和管理 Web 頁面的資源快取、網路請求和響應,從而提供更快速、更穩定的使用者體驗。

image.png

Service Worker 的功能和優點

Service Worker 提供了許多重要功能和優點,其中包括:

1. 離線支援

Service Worker 可以快取 Web 應用程式的資源,使其在斷網或低網路質量環境下仍能夠載入和執行。這意味著使用者可以隨時訪問應用程式,無需依賴網路連線。

2. 更快的載入速度

透過將資源快取在本地,Service Worker 可以顯著提高 Web 頁面的載入速度。它可以從快取中獲取資源,而無需每次都從伺服器重新下載。

3. 支援後臺同步

Service Worker 允許在後臺執行任務,例如資料同步或推送通知。這使得應用程式可以在不干擾使用者的情況下執行一些重要的操作。

4. 增強的安全性

Service Worker 受同源策略的限制,因此它可以提供更安全的資源快取和請求處理。它還可以用於攔截和處理惡意請求。

5. 支援推送通知

Service Worker 具有推送通知功能,可以透過瀏覽器向使用者傳送實時通知,提高使用者參與度和留存率。

使用

// 註冊 Service Worker
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js')
    .then(function(registration) {
      console.log('Service Worker 註冊成功:', registration);
    })
    .catch(function(error) {
      console.log('Service Worker 註冊失敗:', error);
    });
}

// 在 Service Worker 中快取資源
self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('my-cache').then(function(cache) {
      return cache.addAll([
        '/',
        '/index.html',
        '/styles.css',
        '/script.js'
      ]);
    })
  );
});

// 攔截網路請求並從快取中返回資源
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(response) {
      return response || fetch(event.request);
    })
  );
});

我們首先嚐試在瀏覽器中註冊一個 Service Worker,並指定了要快取的資源。然後,在 Service Worker 中,我們透過監聽 install 事件來快取這些資源,並在 fetch 事件中攔截網路請求,從快取中返回資源。這樣,即使在離線時,頁面仍能夠載入所需資源。

生命週期

Service Worker 的生命週期與 web 頁面完全分離。它包括以下幾個階段:

image.png

  • 下載
  • 安裝
  • 啟用

1.下載

使用者首次訪問service worker控制的網站或頁面時,service worker會立刻被下載。瀏覽器會下載包含 Service Worker 的 .js 檔案。

2.安裝

需要在網頁進行註冊來安裝,安裝前需要檢查是否支援 serviceWorker,如果支援,每次頁面載入時就呼叫 register(),瀏覽器將會判斷是否已註冊。 register() 方法的一個重要細節是 Service Worker 檔案的位置。在本例中,可以看到 Service Worker 檔案位於域的根目錄,這意味著 Service Worker 範圍將是這個域下的。換句話說,這個 Service Worker 將為這個域中的所有內容接收 fetch 事件。如果我們在 /example/sw/sw.js 註冊 Service Worker 檔案,那麼 Service Worker 只會看到以 /example/ 開頭的頁面的 fetch 事件(例如 /example/page1/、/example/page2/)。

if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('/sw/sw.js').then(function(registration) {
      // 註冊成功
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }, function(err) {
      // 註冊失敗
      console.log('ServiceWorker registration failed: ', err);
    });
  });
}

註冊成功後,install 事件會被觸發,將會呼叫caches.open() 和我們想要的快取名稱, 之後呼叫 cache.addAll() 並傳入檔案陣列。 這是一個promise 鏈( caches.open() 和 cache.addAll() )。 event.waitUntil() 方法接受一個promise,並使用它來知道安裝需要多長時間,以及它是否成功。 如果成功快取了所有檔案,那麼將安裝 Service Worker。如果其中的一個檔案下載失敗,那麼安裝步驟將失敗。如果快取檔案列表過長,將會增大失敗的機率。

var CACHE_NAME = 'my-cache';
var urlsToCache = [
  '/',
  '/styles/main.css',
  '/script/main.js'
];

self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        console.log('Opened cache');
        return cache.addAll(urlsToCache);
      })
  );
});

3.啟用

接下來就是進入啟用狀態:Activate。 在這個狀態可以更新 Service Worker。

  1. 使用者導航至站點時,瀏覽器會嘗試在後臺重新下載定義 Service Worker 的指令碼檔案。 如果 Service Worker 檔案與其當前所用檔案存在位元組差異,則將其視為新 Service Worker。
  2. 新 Service Worker 將會啟動,且將會觸發 install 事件。
  3. 舊 Service Worker 仍控制著當前頁面,因此新 Service Worker 將進入 waiting 狀態。
  4. 當網站上當前開啟的頁面關閉時,舊 Service Worker 將會被終止,新 Service Worker 將會取得控制權。
  5. 新 Service Worker 取得控制權後,將會觸發其 activate 事件。
self.addEventListener('activate', function(event) {
var cacheAllowlist = ['pages-cache-v1', 'blog-posts-cache-v1'];
event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.map(function(cacheName) {
          if (cacheAllowlist.indexOf(cacheName) === -1) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

快取與請求響應最佳化

策略

  • 快取優先
  • 網路優先
  • 僅使用快取
  • 僅使用網路
  • 速度優先

一旦安裝了 Service Worker 並且使用者導航到其他頁面或重新整理當前頁面,Service Worker 將開始監聽 fetch 事件。

快取優先策略的工作流程:首先,它會監聽瀏覽器的 fetch 事件,攔截原始的請求。接著,它會檢查快取中是否存在即將請求的資源,如果存在,則直接返回快取中的資源。然後,它會發起遠端請求來獲取最新資源,將資源快取起來,並返回給頁面。

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        if (response) {
          return response;
        }
        var fetchRequest = event.request.clone();

        return fetch(fetchRequest).then(
          function(response) {
            if(!response || response.status !== 200 || response.type !== 'basic') {
              return response;
            }

            var responseToCache = response.clone();

            caches.open(CACHE_NAME)
              .then(function(cache) {
                cache.put(event.request, responseToCache);
              });

            return response;
          }
        );
      })
    );
});

什麼是 PWA?

PWA 是一種使用現代 Web API 和傳統的漸進性增強策略來建立跨平臺 Web 應用程式的方法。它結合了 Web 應用程式的可發現性、易安裝性和可連結性,以及原生應用的效能和互動體驗。

優點

漸進性

PWA 適用於所有瀏覽器,因為它是以漸進性增強作為宗旨開發的,使用者無需擔心瀏覽器相容性問題。

連線無關性

PWA 可以在離線或網路較差的情況下正常訪問,依賴於 Service Worker 技術,這使得使用者體驗更穩定。

類原生應用

由於是在 App Shell 模型基礎上開發,PWA 具有與原生應用相似的使用者互動體驗,為使用者提供了更高的滿意度。

持續更新

PWA 始終保持最新狀態,無需使用者手動更新,這消除了版本管理的煩惱。

安全性

透過 HTTPS 協議提供服務,保護使用者資料不被窺探,並確保內容不被篡改。

可索引

PWA 的 manifest 檔案和 Service Worker 可以被搜尋引擎索引,提高應用的可見性。

黏性

透過推送離線通知等功能,PWA 可以吸引使用者迴流,提高使用者參與度。

可安裝

使用者可以將常用的 Web App 新增到桌面,無需前往應用商店下載安裝,提高了可用性。

可連結

透過簡單的連結即可分享內容,無需下載和安裝,便捷實用。

缺點

對系統功能的訪問許可權較低

與原生應用相比,PWA 對裝置的系統功能訪問許可權相對較低,某些高階功能可能受到限制。

沒有統一的審查標準

與應用商店不同,PWA 沒有統一的審查標準,這可能導致一些質量參差不齊的應用進入市場。

核心技術

3. 核心技術

  • Web App Manifest Web App Manifest(Web 應用程式清單)概括地說是一個以 JSON 形式集中書寫頁面相關資訊和配置的檔案。
{
  "name": "My PWA",
  "short_name": "PWA",
  "start_url": "/index.html",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#007bff",
  "icons": [
    {
      "src": "/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}
  • start\_url 可以設定啟動網址
  • icons 會幫我萌設定各個解析度下頁面的圖示
  • background\_color 會設定背景顏色, Chrome 在網路應用啟動後會立即使用此顏色,這一顏色將保留在螢幕上,直至網路應用首次呈現為止
  • theme\_color 會設定主題顏色
  • display 設定啟動樣式
  • Service Worker
  • Notifications API 通知API
  • Push API 推送API 推送 API 可以用來從服務端推送新的內容而無需客戶端介入,它是由應用的 Service Worker 來實現的;通知功能則可以透過 Service Worker 來向使用者展示一些新資訊,或者至少提醒使用者應用已經更新了某些功能。

相關文章