上篇文章整理了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也有調試模式,緩存和命中會輸出在控制枱,可以減輕調試壓力