動態

詳情 返回 返回

PWA離線應用實踐 - 動態 詳情

上篇文章整理了PWA離線應用的技術脈絡和注意事項,本篇文章將記錄使用Vue + Workbox + localforage 實現離線應用的實踐過程。

一、Vue工程改造

Vue CLI 提供了一個官方的 PWA 插件,可以快速為 Vue 項目添加 PWA 支持。運行以下命令安裝插件:

// 在vue工程目錄下執行如下命令
vue add pwa

安裝完成後,插件會自動生成以下文件:
src/registerServiceWorker.js:用於註冊 Service Worker(已在入口文件引入)。
public/manifest.json:Web App Manifest 文件,PWA 的元數據(配置文件中使用)。

在項目根目錄下修改 vue.config.js 文件,配置 PWA 插件的行為。以下是一個示例配置:

module.exports = {
    ...
    pwa: {
    name: '超體',
    themeColor: '#FDEAEC',
    msTileColor: '#000000',
    appleMobileWebAppCapable: 'yes',
    appleMobileWebAppStatusBarStyle: 'black',
    manifestPath: 'https://storage.360buyimg.com/xxx/manifest.json', // 由於部署的緣故,將上面的manifest.json文件路徑替換為了絕對地址
    // 配置 workbox 插件
    workboxPluginMode: 'InjectManifest',
    workboxOptions: {
      importScripts: ['https://storage.googleapis.com/workbox-cdn/releases/5.1.4/workbox-sw.js'], // 引入workbox依賴文件
      exclude: [/\.html$/], // 不緩存的文件,按需配置
      swSrc: 'src/offline/service-worker.js', // 按實際路徑引入
      // ...其它 Workbox 選項...
    }
  }
}

編寫 service-worker.js 文件

importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.1.2/workbox-sw.js')

if (workbox) {
  console.log(`Yay! Workbox is loaded`)
} else {
  console.log(`Boo! Workbox didn't load`)
}

workbox.core.setCacheNameDetails({
  prefix: 'jd-ct', // 緩存前綴
  suffix: 'v0.0.1'
})

workbox.core.skipWaiting() // 強制等待中的 Service Worker 被激活
workbox.core.clientsClaim() // Service Worker 被激活後使其立即獲得頁面控制權
workbox.precaching.precacheAndRoute(self.__precacheManifest || []) // 設置預加載,構建後會生成預加載資源文件 precache-manifest.js

//html採用networkFirst策略,支持離線也能大體訪問
workbox.routing.registerRoute(
    /(.*\.html|.*pf.jd.com(\/?)|.*localhost(\/?)$)/,
    new workbox.strategies.NetworkFirst({
      cacheName: 'jd:ct:html',
      plugins: [
        new workbox.expiration.ExpirationPlugin({
          maxEntries: 10,
        })
      ]
    })
);

// 緩存web的css資源
workbox.routing.registerRoute(
    // Cache CSS files
    /.*\.css/,
    // 使用緩存,但儘快在後台更新
    new workbox.strategies.StaleWhileRevalidate({
      // 使用自定義緩存名稱
      cacheName: 'jd:ct:css',
    })
)

// 緩存web的js資源
workbox.routing.registerRoute(
    // 緩存JS文件
    /(.*\.js|\/gljs)/,
    // 使用緩存,但儘快在後台更新
    new workbox.strategies.StaleWhileRevalidate({
      // 使用自定義緩存名稱
      cacheName: 'jd:ct:js'
    })
)

// 緩存web的json資源
workbox.routing.registerRoute(
    // 緩存JS文件
    /(.*\.json)/,
    // 使用緩存,但儘快在後台更新
    new workbox.strategies.StaleWhileRevalidate({
      // 使用自定義緩存名稱
      cacheName: 'jd:ct:json'
    })
)

// 緩存web的圖片資源
workbox.routing.registerRoute(
    function ({ url }) {
      const reg = /\.(?:png|gif|jpg|jpeg|svg|webp|avif)$/
      const isCache = reg.test(url.href)
      return isCache
    },
    new workbox.strategies.CacheFirst({
      cacheName: 'jd:ct:img',
      plugins: [
        new workbox.expiration.ExpirationPlugin({ // 此處的配置指在優化清除緩存資源
          maxEntries: 60,
          maxAgeSeconds: 30 * 24 * 60 * 60, // 設置緩存有效期為30天
          purgeOnQuotaError: true,
        })
      ],
    })
)

// ** 構建時自動生成,無需編寫 **
// 該文件會記錄構建文件和工程中的一些icon,資源內容需按需引用,畢竟預加載同樣佔用網絡資源
self.__precacheManifest = (self.__precacheManifest || []).concat([
  {
    "revision": "f4421206102fa2971be968f4576b3753",
    "url": "favicon.ico"
  },
  {
    "revision": "d60ab2076b97d8a76e06",
    "url": "js/chunk-vendors.f45b0b1a.js"
  },
  {
    "revision": "d60ab2076b97d8a76e06",
    "url": "js/chunk-vendors.f45b0b1a.js.map"
  },
  {
    "revision": "148cadf1d0b64e5aa7d7",
    "url": "js/index.7058d434.js"
  },
  {
    "revision": "a26142391a19d57bd5745cf1cc159eca",
    "url": "js/three-140.min.js"
  },
  {
    "revision": "f147ae01feb6e9dbc161ae9e1befe720",
    "url": "js/three.js"
  }
]);

至此,我們的PWA應用已經改造完成了,構建後將會看到以下輸出文件 index.html、precache-manifest.js、service-worker.js等

<!DOCTYPE html>
<html lang="">
<head>
  <meta charset="utf-8">
  <link rel="icon" type="image/png" sizes="32x32" href="img/icons/favicon-32x32.png">
  <link rel="icon" type="image/png" sizes="16x16" href="img/icons/favicon-16x16.png">
  <link rel="manifest" href="https://storage.360buyimg.com/arvideo/chaoti/manifest.json">
  <meta name="theme-color" content="#FDEAEC">
  <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="xxx">
  <link rel="apple-touch-icon" href="img/icons/apple-touch-icon-152x152.png">
  <link rel="mask-icon" href="img/icons/safari-pinned-tab.svg" color="#FDEAEC">
  <meta name="msapplication-TileImage" content="img/icons/msapplication-icon-144x144.png">
  <meta name="msapplication-TileColor" content="#000000">
</head>
<body>
</body>
</html>

本地要訪問不能使用file://方式,需要起個本地服務(推薦:http-server)

二、接口離線訪問

通過上面的改造實現了靜態資源的離線訪問,但部分場景可能依賴接口數據才能看到頁面內容,針對此場景採用了indexdb的緩存方案,將部分接口數據存儲到本地,提升應用的離線可用性。

import localforage from 'localforage'

localforage.config({
  driver: [localforage.INDEXEDDB, localforage.WEBSQL, localforage.LOCALSTORAGE],
  name: 'jd-ct',
  description: 'xxx',
})

export const atlasListStore = localforage.createInstance({
  name: 'atlasList',
})

// 判斷網絡連接狀態,按需調用本地數據or接口請求
export const getLocalForageData = (onlineCallback, offlineCallback) => {
  if (navigator.onLine) {
    onlineCallback && onlineCallback()
  } else {
    offlineCallback && offlineCallback()
  }
}
// 業務代碼示例
// 獲取接口數據,並存儲到本地
getAtlasList: function (isRefresh = false) {
      getLocalForageData(() => {
        // 獲取遠程數據
        services.designTool
            .GetDigitalPhotoList(this.searchParams)
            .then((res) => {
              if (res.code === 0) {
                // 更新本地數據
                (res.data.data || []).forEach((item) => {
                    atlasListStore.setItem(String(item.id), item)
                  })
              } else {
                return Promise.reject()
              }
            })
            .catch(() => {
              
            })
      }, () => {
        // 獲取本地數據
        const result = []
        atlasListStore.iterate((value) => {})
            .then(() => {})
            .catch((err) => {
              console.log('catch',err)
            })
      })
    },

三、調試

目前實用chrome對PWA應用調試也是非常方便的:
1)可以在 chrome://serviceworker-internals/ 頁面中查看到全部PWA應用
2)也可以在控制枱中查看
圖片

圖片

圖片

圖片

另外也可以查看indexdb緩存數據
圖片

四、注意事項

1、項目迭代過程中需要維護server worker緩存版本,可通過suffix: 'v0.0.1'更新
2、雖然部分舊版瀏覽器不支持service worker,但在要求不是很高或者用户不是很廣泛的場景下可使用
3、Service Worker 運行在獨立線程中,調試相對複雜,workbox也有調試模式,緩存和命中會輸出在控制枱,可以減輕調試壓力

user avatar dingtongya 頭像 Leesz 頭像 dirackeeko 頭像 aqiongbei 頭像 littlelyon 頭像 dawanzi_6278b06ec111c 頭像 linx 頭像 febobo 頭像 munergs 頭像 assassin 頭像 libubai 頭像 kitty-38 頭像
點贊 124 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.