progressive web apps
Post on 21-Apr-2017
3.120 Views
Preview:
TRANSCRIPT
Progressive�Web�AppsGet�your�site�out�of�the�tab!
송정기�책임�Samsung�Internet�개발자�
웹�표준�에디터�Chromium�contributor
Email:�jungkee.song@samsung.com�GitHub:�@jungkees
Web�App�Manifest
Service�Worker
Push�notification
Web�App�ManifestJinho�Bang�(romandev@nate.com)
웹(Web)은 죽었다.
앱(App)의 시대가 올것이다.
http://stories.flipkart.com/introducing-flipkart-lite/
Progressive�Web�App!
Progressive�Web�AppsBackground�Sync
ServiceWorkerPush�Notification
Permissions
Bluetooth&USB
Web�App�Manifest
Web�App�Manifest?�http://www.w3.org/TR/appmanifest
<link�rel�=�‘manifest’�href=�‘manifest.webmanifest’>
Installable�Web�App� { "name": "Simple web app demo", "short_name": "Demo", "icons": [ { "src": "icon-medium.png", "sizes": "96x96" }, { "src": "icon-large.png", "sizes": "192x192" } ], "theme_color": "#3F51B5", "background_color": "#F5F5F5", "display": "standalone", "orientation": "portrait", "start_url": "/simple-demo/?home=true" }
click
Installable�Web�App� { "name": "Simple web app demo", "short_name": "Demo", "icons": [ { "src": "icon-medium.png", "sizes": "96x96" }, { "src": "icon-large.png", "sizes": "192x192" } ], "theme_color": "#3F51B5", "background_color": "#F5F5F5", "display": "standalone", "orientation": "portrait", "start_url": "/simple-demo/?home=true" }
click
Installable�Web�App� { "name": "Simple web app demo", "short_name": "Demo", "icons": [ { "src": "icon-medium.png", "sizes": "96x96" }, { "src": "icon-large.png", "sizes": "192x192" } ], "theme_color": "#3F51B5", "background_color": "#F5F5F5", "display": "standalone", "orientation": "portrait", "start_url": "/simple-demo/?home=true" }
installed
Installable�Web�App�-�Demo� { "name": "Simple web app demo", "short_name": "Demo", "icons": [ { "src": "icon-medium.png", "sizes": "96x96" }, { "src": "icon-large.png", "sizes": "192x192" } ], "theme_color": "#3F51B5", "background_color": "#F5F5F5", "display": "standalone", "orientation": "portrait", "start_url": "/simple-demo/?home=true" }
Splash�Screen� { "name": "Simple web app demo", "short_name": "Demo", "icons": [ { "src": "icon-medium.png", "sizes": "96x96" }, { "src": "icon-large.png", "sizes": "192x192" } ], "theme_color": "#3F51B5", "background_color": "#F5F5F5", "display": "standalone", "orientation": "portrait", "start_url": "/simple-demo/?home=true" }
background_color
icons name
* Chrome implemented it using name/icon/background_color
Splash�Screen�-�Demo� { "name": "Simple web app demo", "short_name": "Demo", "icons": [ { "src": "icon-medium.png", "sizes": "96x96" }, { "src": "icon-large.png", "sizes": "192x192" } ], "theme_color": "#3F51B5", "background_color": "#F5F5F5", "display": "standalone", "orientation": "portrait", "start_url": "/simple-demo/?home=true" }
Task�Manager� { "name": "Simple web app demo", "short_name": "Demo", "icons": [ { "src": "icon-medium.png", "sizes": "96x96" }, { "src": "icon-large.png", "sizes": "192x192" } ], "theme_color": "#3F51B5", "background_color": "#F5F5F5", "display": "standalone", "orientation": "portrait", "start_url": "/simple-demo/?home=true" }
Display�Mode�(standalone)� { "name": "Simple web app demo", "short_name": "Demo", "icons": [ { "src": "icon-medium.png", "sizes": "96x96" }, { "src": "icon-large.png", "sizes": "192x192" } ], "theme_color": "#3F51B5", "background_color": "#F5F5F5", "display": "standalone", "orientation": "portrait", "start_url": "/simple-demo/?home=true" }
status bar
#3F51B5
Display�Mode�(fullscreen)� { "name": "Simple web app demo", "short_name": "Demo", "icons": [ { "src": "icon-medium.png", "sizes": "96x96" }, { "src": "icon-large.png", "sizes": "192x192" } ], "theme_color": "#3F51B5", "background_color": "#F5F5F5", "display": "fullscreen", "orientation": "portrait", "start_url": "/simple-demo/?home=true" }
no status bar
Start�URL� { "name": "Simple web app demo", "short_name": "Demo", "icons": [ { "src": "icon-medium.png", "sizes": "96x96" }, { "src": "icon-large.png", "sizes": "192x192" } ], "theme_color": "#3F51B5", "background_color": "#F5F5F5", "display": "standalone", "orientation": "portrait", "start_url": "/simple-demo/?home=true" }
../simple_demo?home=true
../simple_demo
Orientation�(landscape)� { "name": "Simple web app demo", "short_name": "Demo", "icons": [ { "src": "icon-medium.png", "sizes": "96x96" }, { "src": "icon-large.png", "sizes": "192x192" } ], "theme_color": "#3F51B5", "background_color": "#F5F5F5", "display": "standalone", "orientation": "landscape", "start_url": "/simple-demo/?home=true" }
Manifest로�Web�App을��first-class�citizen으로�만들자!
Service�WorkerOffline-first�&�Background�processing
오프라인�우선
백그라운드�이벤트�처리
어디가�오프라인?�비행기�안에서만?
가능한�한�언제나!
브라우저에�진짜�없던�것
브라우저�꺼도�web�push�옵니다!
그러면서�웹�그대로!웹�서버�업데이트가�곧�앱�업데이트! URL!�웹의�장점은�바로�거기에
Lie-Fi�시! 데이터�사용할�때도!
push,�sync,�geofenceenter,�..
표준�현황
개발�현황
2015년�6월�네�번째�WD�그리고..
2016년�Service�Worker�1�CR�계획
4.0 40+ 44 32+
Edge? Safari?
동작의�기초가�되는�개념�들
Same�origin�에서�동작
Registration�기반으로�동작��key는�URL�scope!
Navigation�시�매칭되는�SW가�있으면�document가�control�됨
버전�관리를�위해�waiting�worker,�active�worker�존재
fetch��이벤트
Navigation/Resource�request
onfetch
Page
SW
Cache 캐시�match�시도
Match된�Response
클라이언트에�respond
Page Page
이벤트�기반�워커
Navigation/Resource�request
Page
Network�fetch
Response
온라인�이신가요?
충분히�온라인�이신가요?
Navigation/Resource�request
Page
Network�fetch
5XX
DNS�실패
Lie-Fi
너무�심한�RTT
오프라인�with�Service�Worker!
fetch��이벤트
Navigation/Resource�request
onfetch
Page
SW
Cache 캐시�match�시도
Match된�Response
클라이언트에�respond
IDBnew Response({ status: 200, body: { … }})
오프라인�우선!
fetch�이벤트
Navigation/Resource�request
onfetch
Page
SW
Cache
Fetch�request
캐시�match�시도
Match�실패
클라이언트에�respond
인스톨하기var navigator.serviceWorker;sw.register(scriptURL, { scope: scopeURL })
리소스�Pre-caching�하기oninstall = e => { /* 여기서�캐시 */ };
fetch�event�처리�하기onfetch = e => { /* e.request에�대해�개발자�맘대로! */ };
Cache�관리onactivate = e => { /* 안�쓰는�cache delete도�개발자의�몫 */ };
업데이트�w/�24시간�룰매�navigation�시 registration.update() functional�event
“/assets/v1” /assets/v1/serviceworker.js
[ Registration map ]Scope Script URL
Register
Page에서
// scope defaults to ‘/‘ var sw = navigator.serviceWorker;sw.register(‘/assets/v1/serviceworker.js’) .then(reg => { console.log(‘success!’); reg.installing.postMessage(‘Howdy from your installing page.’); }) .catch(e => { console.error(‘Installing the worker failed!:’, e); });
“/bar” /bar/sw1.js
Var sw = navigator.serviceWorker;
① sw.register(‘/bar/sw1.js’, { scope: ‘/bar’ });
② sw.register(‘/foo/sw.js’, { scope: ‘/foo’ });
③ sw.register(‘/bar/sw2.js’, { scope: ‘/bar’ } );
[ Registration map ]Scope Script URL
“/foo” /foo/sw.js
Register��복수개의�registration�생성�가능
Page에서
“/bar” /bar/sw2.js ③ Replace sw1①
②
onactivateSW
oninstall
onfetch
Register가�trigger하는�인스톨�과정
브라우저�internal
oninstall = e => { // pre-cache, etc.};
onactivate = e => { // upgrade Cache, etc.};
onfetch = e => { // Not yet ready};
Fetched�sw�script
Fetch�sw�script
Update① Install②Activate③
onfetch
onfetch = e => { // Not yet ready};
Pre-cache�in�oninstall// sw.js – 인스톨 과정에서 pre-cachingself.addEventListener(‘install’, function(e) { // 명시된 모든 리소스를 성공적으로 받아 캐시 해야만 인스톨 성공 e.waitUntil( self.caches.open(‘shellResources’).then(function(cache) { return cache.addAll([ ‘/app.html’, ‘/assets/v1/base.css’, ‘/assets/v1/app.js’, ‘/assets/v1/logo.png’, ‘/assets/v1/intro_video.webm’ ]); });});
Delete�unused�caches�in�inactivateself.addEventListener('activate', function(event) { event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.filter(function(cacheName) { // Return true if you want to remove this cache, // but remember that caches are shared across // the whole origin }).map(function(cacheName) { return caches.delete(cacheName); }) ); }) );});
https://jakearchibald.com/2014/offline-cookbook/#on-activate
Do�proxy�in�onfetch
self.addEventListener(‘fetch’, function(e) { // 성공적으로 인스톨 되기 전까지는 fetch 이벤트 발생 안 함
// 모든 Cache 함수는 aync 하게 동작 함. 따라서 인터페이스가 promise로 디자인 됨 e.respondWith( self.caches.match(e.request).catch(function() { return fetch(e.request); }).catch(function() { return self.caches.match(‘/fallback.html’); }) );});
onfetch
sw.js
Cache 캐시�match�시도
Match된�response
클라이언트에�respond
“/” /sw.js
[ Registration map ]Scope Script URL
“/foo” /foo/sw.js
Page Navigate�https://example.com/index.html
fetch�이벤트
Scope�matching
Run�SW
클라이언트�Match�(navigation)
onfetchsw.js
Cache 캐시�match�시도
Match된�response
클라이언트에�respond
“/” /sw.js
[ Registration map ]Scope Script URL
“/img” /img/sw.js
Page
Fetch�“https://example.com/img/flower.png
fetch�이벤트
Control
Run�SW
Subresource�request
Emojoy
Offline�Wikipedia
https://wiki-offline.jakearchibald.com/
was�shown
Web�PushNotification
Sanghyun�Park�
-�sh919.park@samsung.com�
(dynastpsh@gmail.com)
PUSH�NOTIFICATION?
PUSH�NOTIFICATION�효과
72% 26%Push�Notification�에�의한��사용자�방문�시간�증가율
Push�Notification�에�의해�방문한�사용자의�지출�증가율
※�Chrome�dev�summit�2015�Keynote�
WEB�PUSH!
Background�Service
WEB�PUSH�NOTIFICATION�필수�요소
Notification�UI
Push�등록/해지/이벤트
Service�Worker
WEB�PUSH�NOTIFICATION�구성
Web�Notification�API
Push�API
WEB�PUSH
=�구독�+�받기
4.0 Chrome 42+
지원�브라우저..
Opera44
어떻게!
Service WorkerClient side proxy
Written in Javascript
Web Server
Push Service
SUBSCRIPTION
①
③
④
②
WHAT!
SUBSCRIPTIONnavigator.serviceWorker.ready.then(function(registration) { registration.pushManager.subscribe({userVisibleOnly: true}) .then(function(subscription) { // The push subscription details needed by the // application. server are now available, and can be // sent to it using, for example, an XMLHttpRequest. sendSubscriptionToServer(subscription); }), function(error) { // Subscription failed. } ); });
WHAT!
SUBSCRIPTION
navigator.serviceWorker.ready.then(function(registration) { registration.pushManager.subscribe({userVisibleOnly: true}) .then(function(subscription) { // The push subscription details needed by the application // server are now available, and can be sent to it using, // for example, an XMLHttpRequest. sendSubscriptionToServer(subscription); }), function(error) { // During development it often helps to log errors to the // console. } ); });
{ "name": "Simple Push Demo", "short_name": "Push Demo", "start_url": "/index.html?homescreen=1", "display": "standalone", "gcm_sender_id": "653317226796"}
Define�GCM�Sender�ID��in�Manifest.json
Service WorkerBackground Service
Web Server
Push Service
①②
③
④
RECEIVING�A�PUSH�MESSAGE
self.addEventListener('push', function(event) { event.waitUntil( fetch('/notification.json').then(function(response) { return response.json(); }).then(function(data) { return self.registration.showNotification(data.title, { body: data.body, icon: data.iconUrl, tag: data.tag, data: { url : data.url } }); }); );});
REFOCUS�EXISTING�WINDOWS
self.addEventListener('notificationclick', function(event) { clients.matchAll({ type: 'window' }) .then(function(clients) { if (clients.length > 0) { clients[0].postMessage({ navigateTo: event.notification.data.url }); return clients[0].focus(); } else { return clients.openWindow(event.notification.data.url); } })});
Demo
발전방향..
Service WorkerClient side proxy
Written in Javascript
Web Server
Push Service
PAYLOAD
DATA DATA
DATA
Q�&�A
참고자료개념�이해
•Introduction�to�Service�Worker•Service�Worker�-�first�draft�published
표준�리소스•Nightly
API�사용�패턴•The�offline�cookbook
•플랫폼�구현자�참고�문서
•GitHub
•Platinum�Service�Worker�Elements
•Service�Worker�API�MDN
top related