本篇主要分享PWA在客路旅行實踐經驗:javascript
主要技術棧:html
本方案更適合已是SPA架構,想要升級PWA的項目,(service-worker.js後面簡稱sw)前端
離線應用vue
能夠去這裏 outweb.io/ 感覺下PWA應用,會看到國內不少出名的站點也陸續升級PWA了java
配置https本地環境利於pwa項目的調試, 本地證書生成能夠參考mkcert。
nginx配置:node
listen 443 ssl;
server_name localhost2;
ssl_certificate /Users/liuze/localhost2.pem;
ssl_certificate_key /Users/liuze/localhost2-key.pem;
複製代碼
這邊是SPA項目,nginx主要作靜態資源輸出,更多關於服務端配置可參考vue 服務端配置:webpack
location @ipad-index {
add_header Cache-Control no-cache;
expires 0;
root /Users/liuze/workspace/klook-fe/klook-gds-hotel-ipad/dist/;
try_files /index.html =404;
}
location /ipad {
alias /Users/liuze/workspace/klook-fe/klook-gds-hotel-ipad/dist/;
autoindex on;
try_files $uri @ipad-index;
}
location /pwa-check { #檢測是否降級處理PWA
proxy_pass http://127.0.0.1:3008;
proxy_pass_request_headers on;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
複製代碼
(注意在 hosts 添加 127.0.0.1 localhost2 映射)nginx
配置主要基於 workbox-webpack-plugin 3.6.3/workbox-sw.js,選擇的緣由以下:git
主要配置基本以下,後面會分析到:github
workboxOptions: {
importScripts:['./sw-prefile.js'],//插入生成的service-worker.js中
runtimeCaching: [{
urlPattern: /v1/,
handler: "networkFirst",
options: {
cacheName: "klook-gds-hotel-ipad-static",
expiration: {
maxEntries: 50,
purgeOnQuotaError: true,
maxAgeSeconds: 60 * 60 * 24,
},
cacheableResponse: {
statuses: [0, 200]
}
}
}],
clientsClaim: true, //新的service-worker 將本身設置爲controller and triggers a "controllerchange" event
offlineGoogleAnalytics: true,
navigateFallback: '/ipad/index.html',
directoryIndex: '/ipad/index.html'
}
複製代碼
workbox-webpack-plugin主要提供兩種模式:
GenerateSW 模式根據配置生成sw文件,適用場景:
InjectManifest 模式經過既有sw文件再加工,適用場景;
這裏使用的GenerateSW模式;
線上部署與本地調試配置相似,除了根據部署項目靜態資源build目錄來調整nginx指向外,還須要進行證書替換
PWA控制頁面,更新不當很容易致使重大頁面錯誤;
這裏選擇用戶主動更新方式;
實際中:
registerServiceWorker.js
//add interval check after registered
registered(registration) {
console.log('Service worker has been registered.')
updateInterval = setInterval(() => {
registration.update();//dynamically pull service-worker
console.log('checking update!')
}, 1000 * 10) // e.g 10s senconds checks
}
//trigger custom event and export resgitration instance
updated(registration) { //triggered whens service-worker.js changed
console.log('New content is available; please refresh.')
document.dispatchEvent(
new CustomEvent('swUpdated', {
detail: registration
})
);
}
複製代碼
App.vue
//add custom event
document.addEventListener(
'swUpdated', this.showRefreshUI, { once: true }
);
//show refresh button
showRefreshUI(e) {
this.registration = e.detail;
this.updateExists = true;
},
//click to refresh and post web worker message
refreshApp() {
this.updateExists = false;
if (!this.registration || !this.registration.waiting) { return; }
this.registration.waiting.postMessage({
type: 'SKIP_WAITING'
});
},
複製代碼
這裏refreshApp主要是經過web worker進行message交互(解決了多tab同步更新問題)
有發送消息確定就有接收方,還記得以前有個配置項:
importScripts:['./sw-prefile.js']
複製代碼
sw-prefile.js
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
複製代碼
主要往sw中注入message事件監聽,使得接收消息時新的sw跳過waiting階段,再結合配置項
clientsClaim:true
複製代碼
完成了新舊sw替換。
到這裏彷佛是皆大歡喜
如今頁面已經由新的sw接管,不過前面的請求經過的舊的sw來,就形成一個頁面存在兩個版本的請求,因此還須要進一步處理
navigator.serviceWorker && navigator.serviceWorker.addEventListener( //triggered by registration.claim
'controllerchange', () => {
if (this.refreshing) return;
this.refreshing = true;
console.log('controllerchange triggered, -> auto refresh!!')
window.location.reload();
}
);
複製代碼
監聽新的sw接管,而後主動觸發一次頁面的刷新,刷新後的就是徹底新的sw接管的頁面
如今主要經過定時發送信息處理降級:
這裏經過node提供/pwa-check接口
Todos:
歡迎糾錯!
關於咱們
客路旅行正在開放前端,後端開發等崗位,這裏工做1075,不打卡
參考資料: