├── .gitignore ├── README.md ├── doc-imgs └── internals.png ├── index.html ├── logging.js ├── package.json ├── reset ├── index.html └── sw.js ├── script.js ├── serviceworker-cache-polyfill.js ├── style.css ├── sw.js └── trollface.svg /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What is this? 2 | 3 | It's a really simple ServiceWorker example. No build systems, (almost) no dependencies. It's designed to be an interactive introduction to the kinds of things you can do with ServiceWorker. 4 | 5 | ## 1. Get it running locally 6 | 7 | Either clone it via git, or just [grab the zip file](https://github.com/jakearchibald/simple-serviceworker-tutorial/archive/gh-pages.zip). 8 | 9 | If you already run a web server locally, put the files there. Or you can run a web server from the terminal for the current directory by installing [node.js](http://nodejs.org/) and running: 10 | 11 | ```sh 12 | npm install 13 | npm start 14 | ``` 15 | 16 | Visit the site in Chrome (`http://localhost:8080` if you used the script above). Open the dev tools and look at the console. Once you refresh the page, it'll be under the ServiceWorker's control. 17 | 18 | **You can reset the SW & caches at any point** by navigating to `reset/`. That will unregister the ServiceWorker & clear all the caches. 19 | 20 | ## 2. Go offline 21 | 22 | Disable your internet connection & shut down your local web server. 23 | 24 | If you refresh the page, it still works, even through you're offline! Well, we're missing that final JavaScript-added paragraph, but we'll fix that shortly. 25 | 26 | Take a look at the code in `index.html` and `sw.js`, hopefully the comments make it easy to follow. 27 | 28 | ## 3. Fixing that script 29 | 30 | The `install` event in the ServiceWorker is setting up the cache, but it's missing a reference to the script that adds the paragraph to the page. Add it to the array. The URL is `script.js`. It doesn't need to be a no-cors request like the Flickr image because it's on the same origin. 31 | 32 | Make sure you're online, refresh the page & watch the console. The browser checks for updates to the ServiceWorker script, if anything in the file has changed it considers it to be a new version. The new version is been picked up, but it isn't ready to use. 33 | 34 | If you open a new tab and go to `chrome://serviceworker-internals` you'll see both the old & new worker listed. 35 | 36 |  37 | 38 | **Not seeing the new worker?** It could be that your server sent the original worker with a far-future `max-age` or similar caching header. Instead, use the node server mentioned in exercise 1 instead. 39 | 40 | Follow the instructions in the page's console to get the new version working. 41 | 42 | Test your page offline, the final JavaScript-added paragraph should have reappeared. 43 | 44 | ## 4. Faster updates! 45 | 46 | The update process you just encountered means only one version of your app can run at once. That's often useful, but we don't need it right now. 47 | 48 | `self.skipWaiting` called within a ServiceWorker means it won't wait for tabs to stop using the old version before it takes over. 49 | 50 | In your `install` event, before the call to `event.waitUntil` add: 51 | 52 | ```js 53 | if (self.skipWaiting) { self.skipWaiting(); } 54 | ``` 55 | 56 | If you refresh the page now, the new version should activate immediately. 57 | 58 | Chrome 40 shipped with ServiceWorker but without `skipWaiting`, so the `if` statement prevents errors there. If you want to see the effects of `skipWaiting`, use a newer version of Chrome, such as [Chrome Canary](https://www.google.com/chrome/browser/canary.html). 59 | 60 | `skipWaiting` means your new worker will handle requests from pages that were loaded with the old worker. If that's a problem, or if multiple tabs running different versions of your app/site is a problem, avoid `skipWaiting`. 61 | 62 | ## 5. Messing around with particular requests 63 | 64 | Currently we're responding to all requests by trying the cache & falling back to the network. Let's do something different for particular URLs. 65 | 66 | In the `fetch` event, before calling `event.respondWith`, add the following code: 67 | 68 | ```js 69 | if (/\.jpg$/.test(event.request.url)) { 70 | event.respondWith(fetch('trollface.svg')); 71 | return; 72 | } 73 | ``` 74 | 75 | Here we're intercepting URLs that end `.jpg` and responding with a network fetch for a different resource. 76 | 77 | Refresh the page, watch the console, and once the new ServiceWorker is active, refresh again. Now you get a different image! 78 | 79 | ## 6. Manual responses 80 | 81 | In the previous step, we handled all requests ending `.jpg`, but often you want finer control over which URLs you handle. 82 | 83 | In the `fetch` event, add the following code before the code you added in the previous exercise: 84 | 85 | ```js 86 | var pageURL = new URL('./', location); 87 | 88 | if (event.request.url === pageURL.href) { 89 | event.respondWith(new Response("Hello world!")) 90 | return; 91 | } 92 | ``` 93 | 94 | Refresh the page, watch the console, and once the new ServiceWorker is active, refresh again. Different response! This is how you create responses manually. 95 | 96 | # Further reading 97 | 98 | You're now cooking with ServiceWorkers! To learn more about how they work, and practical patterns you'll use in apps and sites, check out the resources listed on [is-serviceworker-ready](https://jakearchibald.github.io/isserviceworkerready/resources.html). 99 | -------------------------------------------------------------------------------- /doc-imgs/internals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakearchibald/simple-serviceworker-tutorial/0eb4656b652f75ddf14ea04172c27423c2866979/doc-imgs/internals.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |This is some text.
11 |And now, a picture of an obscene ice-cream:
12 |And now, some content added via JavaScript:
14 | 15 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /logging.js: -------------------------------------------------------------------------------- 1 | // First time playing with SW? This script is just for logging, 2 | // you can pretty much ignore it until you want to dive deeper. 3 | 4 | if (!navigator.serviceWorker.controller) { 5 | console.log("This page is not controlled by a ServiceWorker"); 6 | } 7 | else { 8 | console.log("This page is controlled by a ServiceWorker"); 9 | } 10 | 11 | navigator.serviceWorker.getRegistration().then(function(reg) { 12 | function showWaitingMessage() { 13 | console.log("A new ServiceWorker is waiting to become active. It can't become active now because pages are still open that are controlled by the older version. Either close those tabs, or shift+reload them (which loads them without the ServiceWorker). That will allow the new version to become active, so it'll be used for the next page load."); 14 | } 15 | 16 | reg.addEventListener('updatefound', function() { 17 | console.log("Found a new ServiceWorker!"); 18 | var installing = reg.installing; 19 | reg.installing.addEventListener('statechange', function() { 20 | if (installing.state == 'installed') { 21 | console.log("New ServiceWorker installed."); 22 | // give it a second to see if it activates immediately 23 | setTimeout(function() { 24 | if (installing.state == 'activated') { 25 | console.log("New ServiceWorker activated! Reload to load this page with the new ServiceWorker."); 26 | } 27 | else { 28 | showWaitingMessage(); 29 | } 30 | }, 1000); 31 | } 32 | else if (installing.state == 'redundant') { 33 | console.log("The new worker failed to install - likely an error during install"); 34 | } 35 | }); 36 | }); 37 | 38 | if (reg.waiting) { 39 | showWaitingMessage(); 40 | } 41 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-serviceworker-tutorial", 3 | "private": true, 4 | "scripts": { 5 | "start": "http-server -c-1" 6 | }, 7 | "devDependencies": { 8 | "http-server": "0.8.5" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /reset/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |