Instant and Offline Apps
with Service Worker
Proprietary + Confidential
Changwook Doh
@cwdoh
SKPlanet
Instant Loading
Done.
Proprietary + Confidential
End
Instant Loading*
Asset delivery
…fast
“Every step you
make a user
perform before they
get value out of
your app will cost
you 20% of users”
http://blog.gaborcselle.com/2012/10/every-step-costs-you-20-of-users.html
Performance
= Money
Goals
Goals
1. Don’t be big
Goals
1. Don’t be big
2. Only download
what you need
Goals
1. Don’t be big
2. Only download
what you need
3. Only download
what changed
Best
Practices
! Compression
! Smaller images
○ Multi-sized images
○ Multi-format images
! Reduce Round Trips
○ Redirects
○ Preconnect/Prefetch
! Be interactive
○ async/defer scripts
○ Lazy-load CSS
○ Regioning CSS
○ Defer iFrames
! Good caching
○ Cache forever or not at all
○ Hash in names
! CDNs
! HTTP/2
Compression
goo.gl/hPLUqB
Library Size Compressed size Compression ratio
jquery-1.11.0.js 276 KB 82 KB 70%
jquery-1.11.0.min.js 94 KB 33 KB 65%
angular-1.2.15.js 729 KB 182 KB 75%
angular-1.2.15.min.js 101 KB 37 KB 63%
bootstrap-3.1.1.css 118 KB 18 KB 85%
bootstrap-3.1.1.min.css 98 KB 17 KB 83%
minification	&	gzip
goo.gl/631F31
30% over JPEG
25% over PNG
<picture>

				<source	srcset="washing.webp">

				<source	srcset="washing.jpg">

				<img	src="washing.jpg">

</picture>
<img	sizes="(max-width:	30em)	100vw,

												(max-width:	50em)	50vw,

												calc(33vw	-	100px)"

				srcset="swing-200.jpg	200w,

												swing-400.jpg	400w,

												swing-800.jpg	800w,

												swing-1600.jpg	1600w"

				src="swing-400.jpg"	alt="Kettlebell	Swing">	
goo.gl/Aev18k
Round Trips
+50ms @WiFi
+75ms @LTE
+500ms @3G
+2500ms @2G
Round Trips
100 reqs/site
6 connections
> 833 ms spent in RTT @WiFi
<link	rel="dns-prefetch"	
href="https://example.com/">

<link	rel="preconnect"	
href="https://example.com/">	


<link	rel="preload"		
href="https://example.com/footer.jpg"	
as="image">	
<link	rel="prefetch"		
href="https://example.com/next-page.html"	
as="html">	
goo.gl/Aev18k
Be interactive
<script	async	defer	…>
CSS?
CSS?
github.com/filamentgroup/
loadCSS
Regioning/Critical
…then Rest
https://aerotwist.com/blog/guitar-tuner/
Defer iFrames
<iframe	data-src="https://example.com"></iframe>

<iframe	data-src="https://another.example.com"></iframe>

<script>

document.addEventListener('load',	()	=>	{

		Array.from(document.querySelectorAll('iframe'))

				.forEach(iframe	=>	iframe.src	=	iframe.dataset.src);

});

</script>
github.com/GoogleChrome/simplehttp2server
Offline
Web is a thing like a magic carpet
Your Page
You CAN’T FLY without your page.
Lie-Fi
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
Service Worker
Service Worker fundamentally changes how browser works
! Run a script separate from the page itself
! Run the script even without the page open
! Capture network requests from the page
Service Worker Lifecycle
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
Browser Network
Registering a Service Worker


		navigator.serviceWorker.register('/sw.js');

Registering a Service Worker
if	(navigator.serviceWorker)	{

		navigator.serviceWorker.register('/sw.js');

}
console.log('Hello');
sw.js
DevTools for Service Worker
DevTools > Resources > Service Workers
Check "Update on reload"
Proprietary + Confidential
self.addEventListener('fetch',	event	=>	{

		debugger;

});
sw.js
sw.js
self.addEventListener('fetch',	event	=>	{

		event.respondWith(

				new	Response('Hello	Seoul')

		);

});
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
No network requests
Browser Service Worker Network
fetch()
fetch('/',	{

		method:	'POST',

		body:	data

}).then(res	=>	{

		return	res.json();

}).then(result	=>	{

		return	result;

},	err	=>	{

		console.error(err);

});
sw.js
self.addEventListener('fetch',	event	=>	{

		event.respondWith(

				fetch(event.request)

		);

});
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
Delegating network requests
Browser Service Worker Network
Use cases
! Custom 404 page if not found
! Responsive resource loader
! Cache response and respond without network request
Proprietary + ConfidentialProprietary + Confidential
Proprietary + Confidential
Caching resources
CACHE	MANIFEST

#	2010-06-18:v2



#	Explicitly	cached	'master	entries'.

CACHE:

/favicon.ico

index.html

stylesheet.css

images/logo.png

scripts/main.js



#	Resources	that	require	the	user	to	be	online.

NETWORK:

login.php
Application Cache
cache
self.addEventListener('install',	event	=>	{

		event.waitUntil(

				caches.open('precache-v1')

						.then(cache	=>	cache.addAll([

								'index.html',

								'style.css'

						]))

		);

});
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
Caching resources
Browser Service Worker Network
Respond with cache
self.addEventListener('fetch',	event	=>	{

		event.respondWith(

				caches.match(event.request)

						.then(response	=>	response	||	fetch(event.request))

		);

});
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
sw-precache
! Declaratively write list of resources you want to pre-cache (like AppCache).
! Build with gulp or Grunt to generate a SW file.
! Versioning is automatically taken care of.
sw-precache
sw-precache in gulp
gulp.task('generate-service-worker',	function(callback)	{

		var	path	=	require('path');

		var	swPrecache	=	require('sw-precache');

		var	rootDir	=	'app';



		swPrecache.write(path.join(rootDir,	'service-worker.js'),	{

				staticFileGlobs:	[rootDir	+	'/**/*.{js,html,css,png,jpg,gif}'],

				stripPrefix:	rootDir

		},	callback);

});
Proprietary + ConfidentialProprietary + Confidential
Proprietary + Confidential
Dynamic caching
Loading dynamic data
Changes frequently
Structured data
Unpredictably many query variations
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
Cache first
Browser Service Worker Network
1
4
3
2
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
Network first
Browser Service Worker Network
1
4
2
3
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
sw-toolbox
sw-toolbox
toolbox.router.get('/api',	toolbox.networkFirst);
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
Indexed Database
Proprietary + ConfidentialProprietary + Confidential
Proprietary + Confidential
Putting together
Page Shell
Precache required resources at initial load.
You will at least see app shell on page launch.
Contents
Precache required resources at initial load.
You will at least see app shell on page launch.
Load content using dynamic cache approach.
If the content is not available, use cache.
Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem
Application Shell
THANK YOU

Instant and offline apps with Service Worker