Lighthouse Driven Development PWA
You are seeing the latest revision. If you want to replay step by step, start back to the first commit and checkout each one consecutively.
Run Audit from Chrome.
- Run Chrome Headless (prerequiste: install it!),
- Add an npm script to run audit,
- Add dev dependency in package.json,
- check that audit result is the same as visually running it in Chrome.
- parse JSON results... not quite easy... pwa.auditChecks.score?
"Does not respond with a 200 when offline" "If you're building a Progressive Web App, consider using a service worker so that your app can work offline."
So we are going to add a basic Service Worker!
- Add a service worker to your app.
- Use the service worker to cache files locally.
- When offline, use the service worker as a network proxy to return the locally cached version of the file.
We need to add this to our index.html before our script that generates the UI:
<script>
if('serviceWorker' in navigator) {
navigator.serviceWorker
.register('/sw.js')
.then(function(registration) { console.log("Service Worker Registered with scope: " + registration.scope); })
.catch(function(error) { console.log("Service Worker Registration failed with error: "+ error); });
}
</script>
It checks if the current browser supports Service Workers.
Then it registers a sw.js
file as the code for our Service Worker.
Then it logs some success string in the console.
Now we need to add the ws.js
file:
We can:
- get inspiration from existing PWAs
- code it from scratch
- use a Library like Workbox
- copy paste from Service Worker Cookbook
We will do it from scratch:
this.addEventListener('install', function(event) {
event.waitUntil(
caches.open('v1').then(function(cache) {
return cache.addAll([
'/index.html',
'/',
]);
})
);
});
// SW intercepts "fetch" events, and as a Proxy can intercept a network request.
// thus we can use the cache to return cached stuff instead of making the request.
this.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.catch(function() {
// if CACHE MISSES, then let the request pass.
return fetch(event.request).
then(function(response) {
// And cache this new response
return caches.open('v1').then(function(cache) {
cache.put(event.request, response.clone());
return response;
});
}).catch(function() {
// if fetch is in error, we can here provide a default resource file.
// TODO
});
})
);
});
But a Service Worker will not load on a browser unless it is served from HTTPS. So we need to have our website served from HTTPS
Follow the instructions on local-web-server wiki to have a Green Padlock on Chrome Please note that it will only work for localhost or 127.0.0.1 at the time of writing this file. See the related Issue
We updated our npm script to use this method.
Wow. Just Adding SSL/TLS and using a basic Service Worker that caches resources for offline usage bumped up our PWA score from 27 to 58.
What is a Manifest?
In index.html:
<link rel="manifest" href="/manifest.webmanifest">
The manifest file itself:
{
"name": "LDD-PWA",
"short_name": "LDDPWA",
"start_url": "index.html?pwa=true",
"display": "standalone",
"background_color": "#f0f",
"theme_color": "#f0f",
"description": "From zero to hero PWA!",
"icons": [{
"src": "favicons/favicon-48.png",
"sizes": "48x48",
"type": "image/png"
}, {
"src": "favicons/favicon-72.png",
"sizes": "72x72",
"type": "image/png"
}, {
"src": "favicons/favicon-96.png",
"sizes": "96x96",
"type": "image/png"
}, {
"src": "favicons/favicon-144.png",
"sizes": "144x144",
"type": "image/png"
}, {
"src": "favicons/favicon-168.png",
"sizes": "168x168",
"type": "image/png"
}, {
"src": "favicons/favicon-192.png",
"sizes": "192x192",
"type": "image/png"
}, {
"src": "favicons/pwa-logo.png",
"sizes": "512x512"
}]
}
Then we restart our web server and run the Audit again. We can also check on Application> Manifest in Chrome Dev Tools to see if the manifest is loaded.
We need to update the sw.js to include the icons to be cached, also.
- User can be prompted to install the app
- Configured custom splash screen
- the short_name won't be truncated on the homescreen
"Does not have a tag with width or initial-scale" Let's add this to our index.html
<meta name="viewport" content="width=device-width, initial-scale=1">
Turns out it also fix another error: "Content is sized correctly for the viewport"
We'll add a meta theme-color to our index.html file:
<meta name="theme-color" content="#f0f">
This earns us a few more points to our PWA Score (3 points).
"Does not provide fallback content when JavaScript is not available"
We will simply add a NOSCRIPT tag in our index.html:
<noscript>JavaScript is required for this application to work.</noscript>
Now we need to redirect HTTP traffic to HTTPS. This should be done at the web server level, with some redirect rule in an Apache/NGINX or IIS.
We use 2 Local Web Server instances, one is redirecting everything to HTTPS, the other serves our local app throught HTTP2 and SSL.
I will not (yet) go further on another HTTP server configuration & installation. (for example, using Docker to have an NGINX linked to an Express server etc.)
With those last elements we hit the 100 mark.
To be sure, you have to use ports 80 and 443, so for my part, I had to do some port mapping. My remote web server is on the cloud and I used SSH like this:
# on the remote, I launch `npm run start` which starts the HTTP server (which redirect to HTTPS) and the HTTPS server.
# then on the local server, I run those 2:
sudo ssh -i {path to my SSH key for remote server} -L localhost:80:127.0.0.1:8000 {remote_user_name}@{remote_machine_ip} -Nnt
sudo ssh -i {path to my SSH key for remote server} -L localhost:443:127.0.0.1:8443 {remote_user_name}@{remote_machine_ip} -Nnt
# then I open Chrome and go to http://localhost which gets redirected to https://localhost, and run the Lighthouse Audit
I added other things for SEO, Accessibility, etc. Just look at the differences in the commit history. I won't go into details because it is not related to obtaining the 100 score on PWA.
3 things were added to go back to a perfect π― :
- apple-touch-icon (see index.html)
- maskable icons (see webmanifest)
robots.txt
file for SEO allowing indexation
From here: webmaxru/progressive-web-apps-logo#4
http://favicons.fr/bfb48b598cc988e28990123d1cd81969.htm#Preview
This dumb PWA has Continuous Integration and Deployment with GitHub Actions that will build & deploy to Firebase Hosting, and also then Lighthouse CI will check that the scores are π―.