Skip to content

Commit 987d7cd

Browse files
committed
service worker
1 parent ce06aa6 commit 987d7cd

8 files changed

+187
-634
lines changed

Diff for: README.md

+11-3
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ In addition, I have added setup for:
1515
- persistent storage of redux store across pages and browser refreshes
1616
- material-ui (with customized theme)
1717
- SASS
18-
- service worker registration at root scope
19-
- module aliases (relative paths normally not needed)
18+
- service worker registration at root scope for offline capability
19+
- module aliases (relative paths not needed in modules)
2020
- file compression
2121

2222
## To Install
@@ -30,6 +30,10 @@ In addition, I have added setup for:
3030
git init
3131
git remote set-url origin http://github.com/[YOUR GITHUB NAME]/[YOUR GITHUB NEW REPOSITORY]
3232
33+
34+
test it out:
35+
npm run dev
36+
3337
```
3438

3539

@@ -51,6 +55,7 @@ my-app/
5155
.babelrc
5256
.gitignore
5357
manifest.json
58+
my-service-worker.js
5459
next.config.js
5560
postcss.config.js
5661
routes.js
@@ -173,5 +178,8 @@ Any time that you want to "age" the redux store to make sure that these values w
173178
I don't like using relative paths if I don't have to (I hate trying to remember ../../..)! So I set up in the .babelrc file at the root all the aliases for different folders. If you add a folder to your project, add it in there too.
174179

175180
## Service Worker
176-
Using webpack's SWPrecache Plugin, I set up a service worker at root for PWA support. It is automatically registered on every page load through the Layout component. Be sure to customize the manifest.json file at the root with your project specifics.
181+
I set up a service worker at root (my-service-worker.js).
182+
1. Any time that you add a page, add the url to the cache of the my-service-worker.js file in the install event and change the version number.
183+
2. Be sure to customize the manifest.json file at the root with your project specifics, such as your project name and icons.
184+
177185

Diff for: my-service-worker.js

+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/* Instructions from :
2+
https://css-tricks.com/serviceworker-for-offline/
3+
*/
4+
5+
var version = 'v1::';
6+
7+
//install event
8+
self.addEventListener("install", function(event) {
9+
console.log('service worker installing....');
10+
event.waitUntil(
11+
/* The caches build-in is a promise-based API that helps you to cache responses,
12+
as well as finding and deleting them.
13+
*/
14+
caches
15+
/* You can open a cache by name, and this method returns a promise. We use
16+
a versioned cache name here so that we can remove old cache entries in
17+
one fell swoop later, when phasing out an older service worker.
18+
*/
19+
.open(version + 'fundamentals')
20+
.then(function(cache) {
21+
/* After the cache is opened, we can fill it with the offline fundamentals.
22+
The method below will add all resources we've indicated to the cache,
23+
after making HTTP requests for each of them
24+
*/
25+
return cache.addAll([
26+
'/',
27+
'/about'
28+
]);
29+
})
30+
.then(function() {
31+
console.log('Service worker installed');
32+
})
33+
);
34+
});
35+
36+
//fetch event
37+
self.addEventListener("fetch", function(event) {
38+
console.log('sw fetching...');
39+
/* We should only cache GET requests, and deal with the rest of the methods on the
40+
client side, by handling failed POST requests etc.
41+
*/
42+
if (event.request.method !== 'GET') {
43+
/* if we don't block the event as dhown below, then the requesst will go to
44+
the network as usual.
45+
*/
46+
console.log('SW: fetch event ignored', event.request.method, event.request.url);
47+
return;
48+
}
49+
50+
/* Similar to event.waitUntil in that it blocks the fetch event on a promise.
51+
Fulfillment result will be used as the response, and rejection will end in a
52+
HTTP response indicating failure.
53+
*/
54+
event.respondWith(
55+
caches
56+
/* This method returns a promise that resolves to a cache entry matching
57+
the request. Once the promise is settled, we can then provide a response
58+
to the fetch request.
59+
*/
60+
.match(event.request)
61+
.then(function(cached) {
62+
/* Even if the response is in our cache, we go to the network as well.
63+
This pattern is known for producing "eventually fresh" responses,
64+
where we return cached responses immediately, and meanwhile pull
65+
a network response and store that in the cache.
66+
Read more:
67+
https://ponyfoo.com/articles/progressive-networking-serviceworker
68+
*/
69+
var networked = fetch(event.request)
70+
// We handle the network request with success and failure scenarios.
71+
.then(fetchedFromNetwork, unableToResolve)
72+
// We should catch errors on the fetchedFromNetwork handler as well.
73+
.catch(unableToResolve);
74+
75+
console.log('SW: fetch event ', cached ? '(cached}' : '(network)', event.request.url);
76+
return cached || networked;
77+
78+
function fetchedFromNetwork(response) {
79+
/* We copy the response before replying to the network request.
80+
This is the response that will be stored on the ServiceWorker cache.
81+
*/
82+
var cacheCopy = response.clone();
83+
84+
console.log('SW: fetch from network.' , event.request.url);
85+
86+
caches
87+
//We open a cache to store the response for this request.
88+
.open(version + 'pages')
89+
.then(function add(cache){
90+
/* We store the response for this requst. It'll later become
91+
available to caches.match(event.request) calls, when looking
92+
for cached responses.
93+
*/
94+
cache.put(event.request, cacheCopy);
95+
})
96+
.then(function() {
97+
console.log('SW: fetch response stored in cache.', event.request.url);
98+
});
99+
100+
// Return the response so that the promise is settled in fulfillment.
101+
return response;
102+
}
103+
104+
/* When this method is called, it means we were unable to produce a response
105+
from either the cache or the network. This is our oppotunity to produce
106+
a meaningful response even when all else fails. It's the last chance, so
107+
you probably want to display a "Service Unavailable" view or a generic
108+
error response.
109+
*/
110+
function unableToResolve() {
111+
/* There's a couple of things we can do here:
112+
- Test the Accept header and then return one of the 'offlineFundamentals'
113+
e.g: 'return caches.match('/some/cached/image.png')
114+
- You should also consider the origin. It's easier to decide what
115+
"unavailable" means for requests against your origins than for requests
116+
against a third party, such as an ad provider
117+
- Generate a Response programmatically, as shown below, and return that.
118+
*/
119+
console.log('SW: fetch request failed in both cache and network');
120+
121+
/* Here we're creating a response programmatically. The first parameter is the
122+
response body, and the second one defines the options for the response.
123+
*/
124+
return new Response('<h1>Service Unavailable</h1>', {
125+
status: 503,
126+
statusText: 'Service Uavailable',
127+
headers: new Headers({
128+
'Content-Type': 'text/html'
129+
})
130+
});
131+
}
132+
})
133+
);
134+
});
135+
136+
//activate event
137+
self.addEventListener("activate", function(event) {
138+
/* Just like with the install event, event.waitUntil blocks activate on a promise.
139+
Activation will fail unless the promise is fulfilled.
140+
*/
141+
console.log('SW: activating...');
142+
143+
event.waitUntil(
144+
caches
145+
/* This method returns a promise which will resolve to an array of available
146+
cache keys.
147+
*/
148+
.keys()
149+
.then(function(keys) {
150+
// We return a promise that settles when all outdated caches are deleted.
151+
return Promise.all(
152+
keys
153+
.filter(function (key) {
154+
// Filter by keys that don't start with the latest version prefix.
155+
return !key.startsWith(version);
156+
})
157+
.map(function(key) {
158+
/* Return a promise that's fulfilled
159+
when each outdated cache is deleted.
160+
*/
161+
return caches.delete(key);
162+
})
163+
);
164+
})
165+
.then(function() {
166+
console.log('SW: activate completed');
167+
})
168+
);
169+
});

Diff for: next.config.js

-14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
const path = require('path')
22
const glob = require('glob');
3-
const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
43

54
module.exports = {
65
webpack: (config, { dev }) => {
@@ -33,19 +32,6 @@ module.exports = {
3332
}
3433
);
3534

36-
config.plugins.push(
37-
new SWPrecacheWebpackPlugin({
38-
verbose: true,
39-
staticFileGlobsIgnorePatterns: [/\.next\//],
40-
runtimeCaching: [
41-
{
42-
handler: 'networkFirst',
43-
urlPattern: /^https?.*/
44-
}
45-
]
46-
})
47-
)
48-
4935

5036

5137

Diff for: offline/registerSW.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export default async function registerSW() {
55
try {
66
const registration = await navigator.serviceWorker.getRegistration('/');
77
if (!registration) {
8-
await navigator.serviceWorker.register('/service-worker.js', {
8+
await navigator.serviceWorker.register('/my-service-worker.js', {
99
scope: '/'
1010
});
1111

0 commit comments

Comments
 (0)