A lot of people think that native web its best than web
PWA they combine the best of the web with the best of the app
- reliable (low connection)
- fast (animation)
- engagable (notificaiton, home screen)
Caching strategies
The web has easy access for user and devs. Simple A/B test, build. Fliboard increase +75%. Apps installation happen when you buy the phones. In other countries its a challange to update or install app. A sites don't have that. They add new capabilities. Who add 72%, and +26% users. Flipkart switch from app for a PWA. 2G network, with service worker they improve 3 times the loading.
What make it possible:
- service worker
- web manifest
Its a progressive ehnacemnte and it works as a client side proxy. Apps can work offline. Push message it works with the service worker. Browser doens't need to be open.
Control how the app apears to the users. And also how they can lunched.
PWA separate the content (change often) of the app-shell (don't change often).
App shell its the mnimimal HTML, CSS and JS that the user need to make the first view. The app shell its similar to the bundle code that we put on the app store. Service worker we can focus on speed. We can cache the app shell.
https://weather-pwa-sample.firebaseapp.com/final/index.html
We need to request AJAX.
- Break the design with the core components
- What needs to be on screen
- What other ui component for the app
- What supporting resource are needed (JS, css)
Server-Side Rendering The fastest one but harders
The easiest but the slowest method is to make AJAX requests
So, combination of both. The server inject the data into the apps insteado of pre-redering. THe server injects the data into the javascript.
Every app needs a way to store data. Maybe just user preference or differences relations.
Its easy but its sync, this means that blocks render when we load it.
- Easy to use
- Async
- Fast
But its transactional so it can be subscribed and its not supported.
- Fast
- Asyn
- Fast
But its hard to use. But we can use localforage or lovefield.
Data its not permanent!!
====
Is a JS it runs in your browser in the background. It listen to events from the browser or the server. It intercept fetch events. Proxy btw the network and the browns. They can serve offline or push notifications. In the future will support periodic sync.
** How does a service worker work (Create a graph)
- Web page register sw
- Installing (browser installed when its not instelled yet)
- Activated
- Idle (handle, fetch, push/message or terminated)
Service worker require HTTPS. Only localhost it works.
Intecepet network request, modify or redirect. HTTPS make that secure.
Registering:
if ('serviceWorker' in navigator) {
// the scope is the entire root
navigator.serviceWorker.register('/sw.js')
.then();
}
- Work on icognito.
- chrome//serviceworker-internals
- Application tab on chrome dev tools
- All tabs on the same page use the same service worker. If one new sw is loaded, it will be idle until all the page refresh
When the sw is first register it triggers an install events. Great to pre-cache the resource needed in the app-shell.
var cacheName = 'weatherPWA-v1';
var filesToCache = [...] list of url
self.addEventListener('install', function(e) {
e.waitUntil(
caches.open(cacheName).then(function(cache) {
return cache.addAll(filesToCache); //Atomic operation
});
);
})
The files that it already cached should be cleaned on the activated event
self.addEventListener('activate', function(e) {
e.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if (key !== cacheName && key !== dataCacheName) {
return caches.delete(key)
}
}))
})
)
})
Every change should update the cache.
Fetch events its used to low connection and no network.
self.addEventListener('fetch', function(e) {
console.log('[ServiceWorker] Fetch', e.request.url);
e.respondWith(
caches.match(e.request).then(function (response) {
return response || fetch(e.request);
})
)
})
Pay attention on update SW. The cache can never update.
Responde with first cache otherwise network
Ideal for common resourse. Key components for the App-shell.
Check network first and if it fails it try to field from cache. Its good for api request when you want the updated data. Ideal for content that are updated frequently. Pay attention on the network
Resolve the request only in the cache. It garantee no network will be made to save battery
Fetch from network. Ideal for analytics and stuff that use only network
Request on both it responds with ever respond first. Always made a network request. If the network succed, the cache is updated.
Ideal for data that are updated frequently. Idea to show the cache and then update when the network respond. Pay attention to network dont' respond before the cache. Be careful using it with Data. This strategy say that network response is the source of truth. We need to kick of two parallale request. One for the cache and one for the network.
Its a good practies to separate the resources from the data api.
self.addEventListener('fetch', function(e) {
if (dataApi) {
e.respondWith(
fetch(e.request)
.then(function(response) {
// Update cache with the new data
return caches.open(dataCacheName).then(function(cache) {
cache.puht(e.request.url, response.clone())
return response;
})
})
)
} else {
...
}
})
Sometime using a lib can make thinkgs easier. Service pre-cache its node modules that automagically generating a service worker that pre-cache app-shell resources. Also we can configure run time caching options. We can use grunt or gulp or api. Advantage of automatically run every time you change a script.
npm install sw-precache
swPrecache.write(<'service-worker.js'>, swOptions);
gulp.task('generate-sw', function () {
var swOptions = {
staticFileGlobs: [
'./*.html',
'./images/*.{png,svg,gif,jpg}',
'./scripts/**/*.js',
'./styles/**/*.css'
],
stripPrefix: '.',
runtimeCaching: [{
urlPattern: /^https:\/\/publicdata-weather\.firebaseio\.com/,
handler: 'networkFirst',
options: {
cache: {
name: 'weatherData'
}
}
}]
}
return swPrecache.write('service-worker.js', swOptions);
})
PWA should be reliable fast and engagable.
It can exists outside of the browser. Add to home screen. With an web manifest we can control how it will lunched. Its supported by chrome, opera and firefox.
Its a json to control how the app apears to user. And how it behaviors when its lunched. Supported on chrome and opera. It must contain the name and the short_name.
{
name,
short_name
start_url: /index.html?hs=true,
icons: [{
src,
sizes,
}],
background_color,
theme_color,
display,
orientation
}
Images 48 96 128 144 192 256 384 512
Chrome use 48 and 128.
Validate the web manifest with the manifest validation (manifest-validator.appspot.com)
to use:
<link re=manifest href=manifest.json />
Give the ability to quick and easy to add to home screen. Chrome it shows from some heuristics. It looks for:
- service worker
- web manifest
- Engaged User (2 navigation for at least 5 minutes)
You can defer that. For example an ecommerce can defer it after the user buy something.
Its different we can do same with meta tags.
<link rel="apple-touch-icon" sizes="" href="">
60 76 120 152 167 180
To hide the browser ui component
<meta name="apple-mobile-web-app-capable" content="yes">
minimize status bar
<meta name="apple-mobile-web-status-bar-style" content="black-translucent">
Needs to add some padding for the status bar be visible.
You must use HTTPS to register a sw.
GitHub pages, Firebase (automatically redirect) or Google AppEngine
To deploy to firebase:
firebase login
firebase init
firebase deploy
PWA combine the best of web and the best of APP, it use the great experience that app gives but published on the web.
What Next?
- Offline course
- Javascript Course
- Push notifications
- Web tooling and automation course
- Web Payments API
- Credential Manangement
onpush = function (event) {
var data = event.data.json()
var t = data.title
var opt = {
body: data.body,
icon: data.icon,
tag: data.tag
}
self.registration.showNotification(t, opt)
}
navigator.serviceWorker.register('sw.js')
.then(function(reg) {
reg.pushManager.getSubscription()
.then(function(sub) {
})
})
navigator.serviceWorker.getRegistration().then(function(reg) {
reg.pushManager.subscribe({userVisibleOnly: true})
.thne(function(sub) {
updateServiceWithSubstions(sub)
})
}
githug/GoogleChrome/propel
{
endpoint: "",
keys: {
auth: "",
p256dh
}
}