網路上實作向的資料比較少,更多的是概念以及非常基礎的service worker相關教學,因此將測試及推論的結果做紀錄.

demo:https://parker-nuxt-lab.vercel.app/frontend-api-cach-test

PWA是什麼?

漸進式網頁(Progressive Web Apps,又稱PWA),Google於2016年提出的概念,主要希望能讓網頁能夠更接近app,甚至能夠像是app一樣安裝在裝置上,並且從各方資料來看,完整的PWA除了能安裝於裝置上以外,也應符合RWD及Service Workers相關規範,因此一個良好的PWA應該要提供HTTPS的安全連線,並且只要是能用瀏覽器的任何裝置都能使用,同時可以安裝到裝置上,還要可以在無網路狀態下完全或有限的操作網頁才行.

pwass.png

PWA如何運作?

PWA中最常被提及的概念就是透過Service Worker將網站的資源先行請求後快取在瀏覽器上,讓使用者在切換頁面的時候能夠更快速的取得資源,api的資源也可以,不過經過測試加上查找資料的結果,只能快取HTTP GET的資料,原因是Service Worker的 cache api 只能快取符合資料幂等性的資料,而大多數情況下只有HTTP GET是符合資料幂等性,雖然也有少數資料顯示可以客制化HTTP POST做快取,不過目前仍找不到有效的範本,Google PWA的框架技術 Workbox 的介紹頁面疑似曾經有相關的介紹,不過相關的連結紀錄都已失效,或許需要熟透PWA及Service Worker知識及技術才能更近一步做調整,不過根據 ServiceWorker github 上的 issue ,或許在將來相關規範成熟也可以直接存入GET以外的請求資料.

1_rDRWd4013d8yo8d8r0jWkA.png

PWA如何實作?

由於若要再不依賴任何框架工具的前提下,符合PWA的特性需要深入研究 快取政策 及 Service Worker 更新策略 等相關知識,為求方便入門,本文透過 workbox-build 生成 Workbox 的方式實作PWA,先確定正常建製後的效果為何,才更方便檢驗手刻的版本是否正常

workbox-build 是 Google發布並維護的專屬於 Service Worker 用的打包轉譯工具,由於 Service Worker 最終執行於瀏覽器主程序外,因此 PWA 的 Service Worker 開發完畢後,並不會有任何入口使打包工具去取出並進行打包轉譯,除非直接以不需轉譯的方式轉寫並放置對應的公開位置,不然仍無法正常執行,而 workbox-build 正能解決此問題,並且能更進一步進行minify化,達到進一步效能優化的效果.

workbox-build 在 Nuxt上有出專屬的插件:@vite-pwa/nuxt ,可以直接在 nuxt.config.js 裡透過 pwa 傳入 json物件的方式做設定,導入範例:

export default defineNuxtConfig({
	// ... 省略
  modules: [
		 // ... 省略
    '@vite-pwa/nuxt'
  ],
  pwa: {
    // PWA相關設定
  },
  // ...省略
});

PWA的設定參數概要:

export default defineNuxtConfig({
	// ... 省略
  pwa: {
    injectRegister: 'script-defer',
    manifest: {
      name: 'Parker的Nuxt實驗室',
      short_name: 'Nuxt Lab',
      lang: 'zh-tw',
      icons: [
        {
          src: '/img/ico/apple-touch-icon.png',
          sizes: '180x180',
          type: 'image/png',
          purpose: 'maskable'
        },
        {
          src: '/img/ico/web-app-manifest-192x192.png',
          sizes: '192x192',
          type: 'image/png',
          purpose: 'maskable'
        },
        {
          src: '/img/ico/web-app-manifest-512x512.png',
          sizes: '512x512',
          type: 'image/png',
          purpose: 'maskable'
        },
        {
          src: '/img/ico/web-app-manifest-512x512.png',
          sizes: '512x512',
          type: 'image/png',
          purpose: 'any',
        },
        {
          src: '/img/ico/favicon.ico',
          sizes: '48x48',
          type: 'image/png',
          purpose: 'monochrome'
        }
      ],
      theme_color: '#2c64e3',
      background_color: '#2c64e3',
      display: 'standalone'
    },
    workbox: {
      runtimeCaching: [
        {
          urlPattern: /^https:\\/\\/fonts\\.googleapis\\.com\\/.*/i,
          handler: 'StaleWhileRevalidate',
          options: {
            cacheName: 'google-fonts-cache',
            expiration: {
              maxEntries: 10,
              maxAgeSeconds: 60 * 60 * 24 * 365 // <== 365 days
            },
            cacheableResponse: {
              statuses: [0, 200]
            }
          }
        },
        {
          urlPattern: /^https:\\/\\/fonts\\.gstatic\\.com\\/.*/i,
          handler: 'StaleWhileRevalidate',
          options: {
            cacheName: 'gstatic-fonts-cache',
            expiration: {
              maxEntries: 10,
              maxAgeSeconds: 60 * 60 * 24 * 365 // <== 365 days
            },
            cacheableResponse: {
              statuses: [0, 200]
            },
          }
        }
      ],

      maximumFileSizeToCacheInBytes: 1024 * 1024 * 22, // 22MB
      globPatterns: ['**/*'],
      navigateFallback: null,
    }
  },
  // ...省略
});

PWA效果預覽:

2025-02-2010.17.50-ezgif.com-video-to-gif-converter.gif

可以看到在開發者工具的網路頁籤中,在初次載入就跑了很多次資源呼叫,這就是PWA的Service Workers正在做快取,而第二次再載入時就能看到已經被快取的資源,其大小欄位會顯示 (Service Workers) 的字樣,這時如果再次切到應用程式頁籤中,選擇 Service Workers 選項,就能看到目前的Service Workers的運行狀態,這時可以嘗試斷開網路,部分不依賴即時網路訊號的功能能夠正常運行,這樣PWA的基本功能就都有包含到了.

延伸閱讀

參考資料:

文章目錄:📚Parker Chen 的前端技術碎碎念