本篇主要分享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,不打卡
参考资料: