@@ -15,11 +15,34 @@ description: Advanced recipes to use with Workbox.
15
15
A common UX pattern for progressive web apps is to show a banner when a service
16
16
worker has updated and waiting to install.
17
17
18
- To do this you'll need to add some code to your page and to your service worker.
18
+ To do this you'll need to add some polyfill code to your page and to your service worker.
19
19
20
20
** Add to your page**
21
21
22
22
``` javascript
23
+ // polyfill navigator.serviceWorker.waiting
24
+ if (! (' waiting' in navigator .serviceWorker )) {
25
+ navigator .serviceWorker .waiting = new Promise (function (resolve ) {
26
+ navigator .serviceWorker .ready .then (function (reg ) {
27
+ function awaitStateChange () {
28
+ reg .installing .addEventListener (' statechange' , function () {
29
+ if (this .state === ' installed' ) resolve (reg);
30
+ });
31
+ }
32
+ if (reg .waiting ) resolve (reg);
33
+ if (reg .installing ) awaitStateChange ();
34
+ reg .addEventListener (' updatefound' , awaitStateChange);
35
+ })
36
+ });
37
+ }
38
+
39
+ // polyfill ServiceWorker.skipWaiting()
40
+ if (! (' skipWaiting' in ServiceWorker .prototype )) {
41
+ ServiceWorker .prototype .skipWaiting = function () {
42
+ this .postMessage (' skipWaiting' );
43
+ }
44
+ }
45
+
23
46
function showRefreshUI (registration ) {
24
47
// TODO: Display a toast or refresh UI.
25
48
@@ -40,79 +63,27 @@ function showRefreshUI(registration) {
40
63
41
64
button .disabled = true ;
42
65
43
- registration .waiting .postMessage ( ' force-activate ' );
66
+ registration .waiting .skipWaiting ( );
44
67
});
45
68
46
69
document .body .appendChild (button);
47
70
};
48
71
49
- function onNewServiceWorker (registration , callback ) {
50
- if (registration .waiting ) {
51
- // SW is waiting to activate. Can occur if multiple clients open and
52
- // one of the clients is refreshed.
53
- return callback ();
54
- }
55
-
56
- function listenInstalledStateChange () {
57
- registration .installing .addEventListener (' statechange' , function () {
58
- if (event .target .state === ' installed' ) {
59
- // A new service worker is available, inform the user
60
- callback ();
61
- }
62
- });
63
- };
64
-
65
- if (registration .installing ) {
66
- return listenInstalledStateChange ();
67
- }
68
-
69
- // We are currently controlled so a new SW may be found...
70
- // Add a listener in case a new SW is found,
71
- registration .addEventListener (' updatefound' , listenInstalledStateChange);
72
- }
73
-
74
- window .addEventListener (' load' , function () {
75
- // When the user asks to refresh the UI, we'll need to reload the window
76
- navigator .serviceWorker .addEventListener (' message' , function (event ) {
77
- if (! event .data ) {
78
- return ;
79
- }
80
-
81
- switch (event .data ) {
82
- case ' reload-window' :
83
- window .location .reload ();
84
- break ;
85
- default :
86
- // NOOP
87
- break ;
88
- }
89
- });
90
-
91
- navigator .serviceWorker .register (' /sw.js' )
92
- .then (function (registration ) {
93
- // Track updates to the Service Worker.
94
- if (! navigator .serviceWorker .controller ) {
95
- // The window client isn't currently controlled so it's a new service
96
- // worker that will activate immediately
97
- return ;
98
- }
99
-
100
- onNewServiceWorker (registration, function () {
101
- showRefreshUI (registration);
102
- });
103
- });
72
+ navigator .serviceWorker .addEventListener (' controllerchange' , function () {
73
+ window .location .reload ();
104
74
});
75
+
76
+ navigator .serviceWorker .waiting .then (showRefreshUI);
105
77
```
106
78
107
- This code handles the verious possible lifecycles of the service worker
79
+ The polyfill handles the verious possible lifecycles of the service worker
108
80
and detects when a new service worker has become installed and is waiting to
109
81
activate.
110
82
111
- When a waiting service worker is found we set up a message listener on
112
- ` navigator.serviceWorker ` so we know when to reload the window. When the
113
- user clicks on the UI to refresh the page, we post a message to the new
114
- service worker telling it to force an activation. After this a message is
115
- posted to all windows telling them to reload.
83
+ When a waiting service worker is found we we tell the service worker to skip
84
+ waiting (posting a message if necessary). After this, the ` controllerchange `
85
+ event will fire. The page should reload when the controller changes to
86
+ ensure that all tabs are running the same version of the code.
116
87
117
88
Note: This is one possible approach to refreshing the page on a new service
118
89
worker. For a more thorough answer as well as an explanation of alternative
@@ -122,19 +93,18 @@ discuess a range of options.
122
93
123
94
** Add to your service worker**
124
95
96
+ This code supports the polyfill while we wait for ` ServiceWorker.skipWaiting `
97
+ to be available in all browsers.
98
+
125
99
``` javascript
126
100
self .addEventListener (' message' , (event ) => {
127
101
if (! event .data ){
128
102
return ;
129
103
}
130
104
131
105
switch (event .data ) {
132
- case ' force-activate ' :
106
+ case ' skipWaiting ' :
133
107
self .skipWaiting ();
134
- self .clients .claim ();
135
- self .clients .matchAll ().then ((clients ) => {
136
- clients .forEach ((client ) => client .postMessage (' reload-window' ));
137
- });
138
108
break ;
139
109
default :
140
110
// NOOP
@@ -143,10 +113,7 @@ self.addEventListener('message', (event) => {
143
113
});
144
114
```
145
115
146
- This will receive a the 'force-activate' message and call ` skipWaiting() ` and
147
- ` clients.claim() ` so that the service worker activates immediately and controls
148
- all of the currently open windows. We then message each window with a
149
- 'reload-window' message so each tab is refreshed with the latest content.
116
+ This will receive a 'skipWaiting' message and call ` skipWaiting() ` .
150
117
151
118
## "Warm" the runtime cache
152
119
0 commit comments