Skip to content

Service Worker presentation - Oodrive R&Days / Codecamp

Notifications You must be signed in to change notification settings

adriengibrat/ServiceWorker.md

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

title: Service Worker author: name: I'm Adrien, frontend developer url: https://oodrive.com email: a.gibrat@oodrive.com theme: ./theme controls: false output: index.html

--

partners

<script type="presenter/text"> I work at Oodrive an international software editor We create B2B products to securly share, backup & sign documents in the cloud Our headquarters are in Paris, France We are also here in Iasi, with a great team of developers called the "Iron Team" (backend, frontend and QA) Thank you to have invited me and my french accent ;) Please, bear with me because it's my first talk with such a large audience and also my very first talk in english Yes, I do like challenge </script>

--

Service Worker

A [gentle] introduction

<script type="presenter/text"> Today I want to make you discover Service worker because it push the web plateform forwards & allows to do pretty cool stuffs! It's getting more traction every day and it will be available *very soon* in all evergreen browsers You should have heard about Service Worker If not, you may have heard about Progressive Web Apps: It's a web page that try to behave like a native application Service worker is the core technology that enable this kind of magic ;) </script>

--

What is a Worker?

<script type="presenter/text"> But first, lets start with the basics </script>

--

Browsers used to run everything in one thread

sequenceDiagram
	participant B as πŸ—” Browser
	participant X
	B->X: HTTP requests, compute, DOM changes ...
Loading

πŸ‹ Intensive Javascript

<script> document.currentScript.parentNode.addEventListener('mermaid', event => event.target.querySelectorAll('g:nth-child(4), line').forEach(e => e.style = 'visibility: hidden!important')) // hide lines </script> <script type="presenter/text"> Each document in a browser is a single tread that do a lot of stuffs Modern Javascript engines are very good to deal with blocking and async executions But sometimes it blocks render (bad user experience) side node: links in every slide </script>

--

Workers runs Javascript in another thread

sequenceDiagram
	participant B as πŸ—” Browser
	participant W as πŸ“ƒ Worker
	B->>W: create
Loading

πŸ““ Worker API

<script type="presenter/text"> That's why Worker allows to unload the document thread by delegating some heavy work to an independant script To do so, the main thread creates a new dedicated thread thanks to a worker </script>

--

Then uses events to communicate

sequenceDiagram
	participant B as πŸ—” Browser
	participant W as πŸ“ƒ Worker
	B->>W: create
	loop events
		W--xB: message
		B--xW: 
	end
Loading

πŸ“― postMessage

<script type="presenter/text"> Then the worker and the document can communicate through events Usually messages transit by using postMessage method It allows to exchange strings and to some extent Objects (more about this later) </script>

--

There are three kind of Workers

All run background scripts independently of any UI

  • Web Workers since 2010, perform heavy computation, ex: pdfjs
  • Shared Workers since 2010, shared across same origin webkit in 2015
  • Service Workers since 2014, control HTTP requests from same origin

πŸ‘· Workers

<script type="presenter/text"> -> Each Web Worker is dedicated to its 'parent' thread -> Shared Worker can communicate with every document from the same orign They are not deprecated, but they won't gain traction without at least positive signal about better browser support -> Service worker are also shared across same origin, but have complete different goals and propeties </script>

--

Let's talk about Service workers

<script type="presenter/text"> Ok, enough teasing ;) </script>

--

Definition

A service worker acts as a proxy between the browser (your web application) and the network

Main usages

  • Offline access
graph LR
	B("πŸ—”") --> S("πŸ“ƒ")
    S -.- N("☁")
	style B fill:none,stroke:none
	style S fill:none,stroke:none
	style N fill:none,stroke:none
Loading
  • Push notifications
graph RL
	N("πŸ—¨") --> S("πŸ“ƒ")
	S -.- B("πŸ—”")
	style B fill:none,stroke:none
	style S fill:none,stroke:none
	style N fill:none,stroke:none
Loading
  • and more...
  • πŸ“– Service Worker API

    <script> document.currentScript.parentNode.addEventListener('mermaid', event => { event.target.classList.add('fragment', 'float-right') event.target.style.marginTop = '-5rem' event.target.style.maxWidth = '60%' if (event.target.matches('li:nth-of-type(2) svg')) event.target.style.marginTop = '-8rem' }) // graphs position </script> <script type="presenter/text"> -> Service worker can respond to HTTP requests even when network is down -> Service worker can recieve push when your web app is not opened in any browser tab or window The main purpose is offline access, but as a proxy it also enables creative uses, like on-the-fly image generation Ex: The Guardian used Service Worker to display UK election results per candidates, per distrinct with Push Notification They composed localy candidate avatar with other graphic information in large images displayed as 'Big Pictures' Push Notification saving up to 100MB on devices with high pixel density -> We'll see other exiting new usages at the end of the talk </script>

    --

    It uses recent async APIs

    support: chrome, mozilla, android, opera, edge, safari

    • Promises construction to execute code after a task is fulfilled
    • postMessage method to communicate between contexts
    • fetch function to make simple HTTP requests, successor of XHR

    βœ“ Is Service Worker Ready

    <script type="presenter/text"> -> a Promise is simple abstraction of an asynchronous task -> postMessage can exchange strings and some Objects (ArrayBuffer, MessageChannel Port or ImageBitmap) Ownership of the object is transferred, it becomes unusable in the context it came from -> fetch is really simple to use compared to the bloated XHR Object It's a function that accepts 2 arguments (URL and options) and return a promise side note: explain support icons </script>

    --

    Runs under conditions

    support: chrome, mozilla, android, opera, edge (development), safari (development)

    • HTTPS only, same origin for security reason
    • No DOM access no document, nor window
    • Behind flag in Edge & Safari Technical Preview

    ❔ Can I Use Service Worker

    <script type="presenter/text"> -> It's also not available in private browsing in Firefox, i guess it's how security is implemented -> Remember, a worker has no access to UI The global object is `self` -> It means you can use service worker in all evergreen browsers today And start to plan cross browser features based on Service Worker for literally tomorrow </script>

    --

    How to use it?

    <script type="presenter/text"> Yeah, I know you'd like to see some code... </script>

    --

    Registration

    support: chrome, mozilla, android, opera, edge (development), safari (development)

    main.js

    if (navigator.serviceWorker)
    	navigator.serviceWorker
    		.register('/service-worker.js', { scope: '/' })
    		.then(registration => registration.state)

    Multiple Service Workers must register distinct scopes

    🍰 Getting Started

    <script type="presenter/text"> The first thing you need to do is to register your service worker Provide a script URL, and optional settings By default the scope is the path of the Service Worker script The scope is the path on your origin behind which the Service Worker will be active Like the path for a cookie -> Multiple Service Workers must register distinct scopes You may have a SW dedicated to assets with the '/public' scope And another one dedicated to data requests behind the '/api' scope In the end, your get a registration Object that gives you access to the state of the Service Worker amongst other things </script>

    --

    First install

    support: chrome, mozilla, android, opera, edge (development), safari (development)

    service-worker.js

    self.addEventListener('install', event => 
    	event.waitUntil(/* ready to activate */)
    )
    graph LR
    	P["πŸ—” page"] -. register .-> I("βš™ install")
        I --> A("βœ” activate")
        I --> E("⚠ error")
    	style P fill:white
    	style I fill:white,stroke:#41b6e8
    	style A fill:white,stroke:green
    	style E fill:white,stroke:red
    
    Loading

    βš™ Service Worker Lifecycle

    <script type="presenter/text"> Service Worker nee to be installed because it will run independently of your web application The `self` keyword is the Service Worker instance The `waitUntil` method takes a Promise as only argument, it ensure that you can do whatever you need before the Service Worker activates. ex: prefill the cache, load external, etc </script>

    --

    Next visit

    support: chrome, mozilla, android, opera, edge (development), safari (development)

    • Activates only inside scope
    • Skip the install event handler
    graph LR
    	P["πŸ—” page"] -. in scope .-> A("βœ” activate")
    	A -. control .-> P
    	click P callback "in the Service Worker Scope"
    	style P fill:white,stroke:lightgrey
    	style A fill:white,stroke:green
    
    Loading

    πŸ‘ Service Worker: An Introduction

    --

    Upgrade process

    support: chrome, mozilla, android, opera, edge (development), safari (development)

    New Service Worker will wait deactivation of the old one before it activates

    graph LR
    	subgraph Old Service Worker
        	O("βœ” activate") --> D("☠ deactivate")
        end
    	subgraph New Service Worker
    		O("βœ” activate") -. updatefound .-> I("βš™ install")
    		I -. waiting .-> A("βœ” activate")
        end
    	style O fill:white,stroke:lightgrey
    	style D fill:white,stroke:red
    	style I fill:white,stroke:#41b6e8
    	style A fill:white,stroke:green
    
    Loading
    <script> document.currentScript.parentNode.addEventListener('mermaid', event => event.target.style.maxWidth = '90%') </script>

    ✌ Service Worker Lifecycle

    --

    Emitted events

    Main functional events

    • fetch intercepted an HTTP request (made by main thread)
    • message received a message via postMessage
    • push received a push notification

    πŸ—± Service Worker events

    --

    States & events flow

    graph LR
    	A("βœ” Activated") --> D{"βŒ› Idle"}
    	D --> F("βš™ Handle event")
    	F --> D
        D --> T["☠ Terminated"]
    	T --> F
    	style A fill:white,stroke:green
    	style D fill:white
    	style F fill:white,stroke:#41b6e8
        style T fill:white,stroke:red
    
    Loading
    <script> document.currentScript.parentNode.addEventListener('mermaid', event => event.target.style.maxWidth = '100%') </script>

    Service worker may terminate at any time!

    🌠 Using Service Workers

    --

    Avoid pitfalls

    --

    Persistence

    You cannot rely on global state within a service worker

    • No Local Storage
    • But IndexedDB (use a wrapper!)
    • Use the Cache interface

    πŸ–₯ Localforage

    --

    Tips

    • Beware of redirects, credentials, streams
    • Avoid puting version in script URL
    • Specific debug tools chrome://serviceworker-internals
    • By default first run does not control the main Thread

    ⚠ Service Workers gotchas

    --

    Let's go offline!

    --

    Offline basics

    support: chrome, mozilla, android, opera, edge (development), safari (development)

    service-worker.js

    const CACHE = 'my-awsome-pwa',
    const FILES = ['/', '/styles.css', '/script.js']
    self.addEventListener('install', event =>
    	event.waitUntil(
    		caches.open(CACHE)
    			.then(cache => cache.addAll(FILES))
    	)
    )

    πŸ“š Service Worker specification

    --

    Cache first, fallback to network

    support: chrome, mozilla, android, opera, edge (development), safari (development)

    service-worker.js

    self.addEventListener('fetch', event =>
    	event.respondWith(
    		caches.match(event.request)
    			.then(response => response || fetch(event.request))
    	)
    )

    πŸ“΄ Offline cookbook

    --

    Caching strategies

    • Cache only KISS Progressive Web Application
    • Cache first, falling back to network static assets
    • Network first, falling back to cache API resources
    • etc

    πŸ—ƒ Workboxjs

    --

    Receive notifications

    --

    Web push notification

    • Used to be complex vendor specific implementations
    • Fully standardized browser provides push server
    • Native notifications UI with user permission

    🚨 Push Notifications in Web App

    --

    Subscription

    support: chrome, mozilla, android, opera, edge (development), safari (development)

    main.js

    const registration = await navigator.serviceWorker.ready
    const push = await registration.pushManager.subscribe()
    sendToBackend(push.endpoint)

    manifest.json (chrome only)

    { ..., "gcm_sender_id": "<Your Sender ID Here>" }

    πŸ“² Web Push Notifications

    --

    And more!

    • Background sync wait for stable network connection (backoff)
    • Periodic background sync scheduled synch (in design)
    • Background fetch HTTP request outlives browser close

    🐢 Background-fetching proposal

    --

    Thanks!

    πŸ‘ Slides links

    --

    feedback

    --

    Life cycle example

    sequenceDiagram
    	participant B as πŸ—” Browser
    	participant W as πŸ“ƒ Service Worker
    	participant S as ➑ Push Server
    	B->>W: register
    	activate B
    	activate W
    	W--xB: message
    	deactivate W
    	B--xW: fetch
    	activate W
    	deactivate W
    	deactivate B
    	S--xW: notification
    	activate W
    	W--xB: 
    	deactivate W
    
    Loading
    <script> document.currentScript.previousElementSibling.style.maxWidth = '85%' // Fix arrow document.currentScript.parentNode.addEventListener('mermaid', event => { event.target.querySelectorAll('line[stroke-width][marker-end]').forEach((line, index) => index % 2 || (line.x2.baseVal.value -= 5)) }) </script>

    About

    Service Worker presentation - Oodrive R&Days / Codecamp

    Topics

    Resources

    Stars

    Watchers

    Forks

    Releases

    No releases published

    Packages

    No packages published