Skip to content

Commit 6be76ed

Browse files
committed
feat(pwa): 增加 pwa 服务弹框功能
1 parent 2b357d3 commit 6be76ed

File tree

5 files changed

+136
-4
lines changed

5 files changed

+136
-4
lines changed

src/App.vue

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
<template>
22
<div id="app">
33
<router-view />
4+
<service-worker-update-popup />
45
</div>
56
</template>
67

78
<script lang="ts">
8-
import {Vue, Component} from 'vue-property-decorator';
9+
import {Vue, Component} from 'vue-property-decorator';
10+
import ServiceWorkerUpdatePopup from '@/pwa/components/ServiceWorkerUpdatePopup.vue';
911
10-
@Component
11-
export default class App extends Vue {}
12+
@Component({
13+
name: 'App',
14+
components: {
15+
ServiceWorkerUpdatePopup
16+
}
17+
})
18+
export default class App extends Vue {}
1219
</script>
1320

1421
<style lang="less">

src/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import './router/router.interceptor';
1313
import './filters';
1414
import './directives';
1515
import './services';
16-
import './registerServiceWorker';
16+
import './pwa/register-service-worker';
1717
// mock
1818
import { mockXHR } from '../mock';
1919

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<script lang="ts">
2+
import { Component, Vue } from 'vue-property-decorator';
3+
4+
@Component({
5+
name: 'ServiceWorkerUpdatePopup'
6+
})
7+
export default class extends Vue {
8+
private refreshing = false;
9+
private notificationText = 'New content is available!';
10+
private refreshButtonText = 'Refresh';
11+
private registration: ServiceWorkerRegistration | null = null;
12+
13+
created() {
14+
// Listen for swUpdated event and display refresh notification as required.
15+
document.addEventListener('swUpdated', this.showRefreshUI, {once: true});
16+
// Refresh all open app tabs when a new service worker is installed.
17+
navigator.serviceWorker.addEventListener('controllerchange', () => {
18+
if (this.refreshing) return;
19+
this.refreshing = true;
20+
window.location.reload();
21+
});
22+
}
23+
24+
render() {
25+
// Avoid warning for missing template
26+
}
27+
28+
private showRefreshUI(e: Event) {
29+
// Display a notification inviting the user to refresh/reload the app due
30+
// to an app update being available.
31+
// The new service worker is installed, but not yet active.
32+
// Store the ServiceWorkerRegistration instance for later use.
33+
const h = this.$createElement;
34+
this.registration = (e as CustomEvent).detail;
35+
this.$notify.info({
36+
title: 'Update available',
37+
message: h('div', {class: 'sw-update-popup'}, [
38+
this.notificationText,
39+
h('br'),
40+
h('button', {
41+
on: {
42+
click: (e: Event) => {
43+
e.preventDefault();
44+
this.refreshApp();
45+
}
46+
}
47+
}, this.refreshButtonText)
48+
]),
49+
position: 'bottom-right',
50+
duration: 0
51+
});
52+
}
53+
54+
private refreshApp() {
55+
// Protect against missing registration.waiting.
56+
if (!this.registration || !this.registration.waiting) return;
57+
this.registration.waiting.postMessage('skipWaiting');
58+
}
59+
}
60+
</script>
61+
62+
<style lang="scss" scoped>
63+
.sw-update-popup > button {
64+
margin-top: 0.5em;
65+
padding: 0.25em 1.5em;
66+
}
67+
</style>

src/pwa/register-service-worker.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/* eslint-disable no-console */
2+
3+
import { register } from 'register-service-worker';
4+
5+
if (process.env.NODE_ENV === 'production') {
6+
register(`${process.env.BASE_URL}service-worker.js`, {
7+
ready() {
8+
console.log(
9+
'App is being served from cache by a service worker.\n' +
10+
'For more details, visit https://goo.gl/AFskqB'
11+
);
12+
},
13+
registered(registration) {
14+
console.log('Service worker has been registered.');
15+
// Routinely check for app updates by testing for a new service worker.
16+
setInterval(() => {
17+
registration.update();
18+
}, 1000 * 60 * 60); // hourly checks
19+
},
20+
cached() {
21+
console.log('Content has been cached for offline use.');
22+
},
23+
updatefound() {
24+
console.log('New content is downloading.');
25+
},
26+
updated(registration) {
27+
console.log('New content is available; please refresh.');
28+
// Add a custom event and dispatch it.
29+
// Used to display of a 'refresh' banner following a service worker update.
30+
// Set the event payload to the service worker registration object.
31+
document.dispatchEvent(
32+
new CustomEvent('swUpdated', {detail: registration})
33+
);
34+
},
35+
offline() {
36+
console.log('No internet connection found. App is running in offline mode.');
37+
},
38+
error(error) {
39+
console.error('Error during service worker registration:', error);
40+
}
41+
});
42+
}

src/pwa/service-worker.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// This is the code piece that GenerateSW mode can't provide for us.
2+
// This code listens for the user's confirmation to update the app.
3+
self.addEventListener('message', (e) => {
4+
if (e.data) {
5+
if (e.data === 'skipWaiting') {
6+
self.skipWaiting();
7+
}
8+
}
9+
});
10+
11+
workbox.clientsClaim();
12+
13+
// The precaching code provided by Workbox. You don't need to change this part.
14+
self.__precacheManifest = [].concat(self.__precacheManifest || []);
15+
workbox.precaching.suppressWarnings();
16+
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});

0 commit comments

Comments
 (0)