There are a lot of articles about PWA that tell in detail what it is and how to set it up. Yet you may not always understand them from the first time.
My goal is to provide basic information in simple language that will be enough for you to understand what PWA is. Then to show you a real example to strengthen everything that you learned. Thus, take your laptop and follow me.
After reading it you will understand the following:
As with any technology, PWA has its benefits and limitations. Before covering them, let’s understand what PWA is.
PWA technology was announced by Google in 2015. It positions itself as an additional add-on that allows you to make the site look like a mobile app.
At the same time, the interior does not change, does not transform, the site remains the same, only the browser is transformed.
Also, you should know for which type of business the PWA is a better solution than a mobile application. You could find more information about the differences between PWA and mobile app here.
What can PWA do?
<div>
with the “app” — ID. Here is just at the moment when everything is becoming stupid, SEO analysis occurs, but the page, as you understand, is empty. And even if you add +100500 PWA to the site, they will not speed up the rendering of HTML code. And to be less unsubstantiated, let’s make sure by a real example. Let’s take the site https://madops.io, which is a single page application. If you look at its inside view-source: https://madops.io, you’ll see everything I described above. In other cases when the server renders all the HTML markup at once there are no problems, as, for example, here view-source: https://maddevs.io.If you want to read more about what PWA is please check this link. Here are the sites that use PWA.
Before going deeply in PWA set up, let’s figure out its basic concepts and it’s components
PWA is really simple in set up. So let’s start right from writing the code!
No, wait.
Here is a link to the ready-made code https://github.com/denisoed/PWA-example. Here you can download the images that will be required further, well, for the one you will get acquainted with what happened.
Firstly you need to create a folder in the project and name it PWA, for example. Then add it to this folder index.html, which will contain the following code:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>PWA</title>
<meta name="description" content="Progressive
Web Apps">
</head>
<body class="fullscreen">
<div class="container">
<a href="https://maddevs.io" target="_blank">
<img src="./images/logo.svg" alt="Mad Devs">
</a>
<h1>PWA</h1>
<p>Progressive Web Apps</p>
</div>
</body>
</html>
I’ve already prepared the layout, but it looks bad without styles, so we’ll add them as well. Creating a CSS folder where we add the styles.css file and insert the code below:
body {
font-family: sans-serif;
}
/* Make content area fill the entire browser window
*/
html,
.fullscreen {
display: flex;
height: 100%;
margin: 0;
padding: 0;
width: 100%;
background-color: #000;
}
/* Center the content in the browser window */
.container {
margin: auto;
text-align: center;
}
.container img {
width: 50px;
height: auto;
}
.container h1 {
color: #fff;
font-size: 12rem;
font-weight: bold;
margin: 30px 0 -20px;
}
.container p {
color: #fff;
font-size: 3rem;
margin: 0;
}
Then connect this file to index.html, in the <head>…</head> tag
<link rel="stylesheet" href="css/styles.css">
Let’s immediately connect the necessary images, which can be downloaded here. Click on the link, there will be a button Clone or download, green such, poke it, then poke Download ZIP. The archive will be downloaded and there will be images in the images folder. Phew, I think I explained it quite clearly:
You open the project, create the images directory there, where you insert all the images. Then open it index.html and insert meta information into the <head>…</head> tag. What it is and why you can read here.
<link rel="icon" href="images/favicon.ico"
type="image/x-icon" />
<link rel="apple-touch-icon" href="images/mstile-
150x150.png">
<meta name="theme-color" content="black" />
<meta name="apple-mobile-web-app-capable"
content="yes">
<meta name="apple-mobile-web-app-status-bar-style"
content="black">
<meta name="apple-mobile-web-app-title"
content="PWA">
<meta name="msapplication-TileImage"
content="images/mstile-144x144.png">
<meta name="msapplication-TileColor"
content="#FFFFFF">
<meta name="viewport" content="width=device-width,
initial-scale=1.0">
As a result, in the file index.html, there should be a structure like this:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>PWA</title>
<meta name="description" content="Progressive
Web Apps">
<link rel="icon" href="images/favicon.ico"
type="image/x-icon" />
<link rel="apple-touch-icon"
href="images/mstile-150x150.png">
<meta name="theme-color" content="black" />
<meta name="apple-mobile-web-app-capable"
content="yes">
<meta name="apple-mobile-web-app-status-bar-
style" content="black">
<meta name="apple-mobile-web-app-title"
content="PWA">
<meta name="msapplication-TileImage"
content="images/mstile-144x144.png">
<meta name="msapplication-TileColor"
content="#000">
<meta name="viewport" content="width=device-
width, initial-scale=1.0">
<link rel="stylesheet" href="css/styles.css">
</head>
<body class="fullscreen">
<div class="container">
<a href="https://maddevs.io" target="_blank">
<img src="./images/logo.svg" alt="Mad
Devs">
</a>
<h1>PWA</h1>
<p>Progressive Web Apps</p>
</div>
</body>
</html>
Now it remains to run and see what happened. I have found a very convenient extension Web Server for Chrome that runs a local server, you need to install it, we will need it next. There is nothing difficult, just specify the folder with the project where it is index.html he’ll do it himself. Copy the link and paste it into the browser.
And here’s what we got. I would not say that this is fine, but how normal for me!
Well, listen, the most difficult thing, consider it done, let’s now see what google validation thinks about our work. To do this, press f12 and go to the Lighthouse tab (before Google updated, this tab named Audits), there will be a blue Generate report button, poke.
After the validation process is completed, we will see the following picture: the item responsible for PWA will be gray. This means that we do not have any settings.
And if you scroll down, you can see the recommendations that you need to follow in order for PWA to work like clockwork.
The Lighthouse tab will help you track all errors when configuring PWA.
First, you need to create a manifest.json file in the root of the project. We add the following metadata to it:
{
"name": "Progressive Web Apps",
"short_name": "PWA",
"icons": [
{
"src": "images/mstile-70x70.png",
"sizes": "70x70",
"type": "image/png"
},
{
"src": "images/mstile-144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "images/mstile-150x150.png",
"sizes": "150x150",
"type": "image/png"
},
{
"src": "images/mstile-192x192.png",
"sizes": "310x150",
"type": "image/png"
},
{
"src": "images/mstile-310x150.png",
"sizes": "310x150",
"type": "image/png"
},
{
"src": "images/mstile-310x310.png",
"sizes": "310x310",
"type": "image/png"
},
{
"src": "images/mstile-512x512.png",
"sizes": "310x310",
"type": "image/png"
}
],
"lang": "en-US",
"start_url": "/index.html",
"display": "standalone",
"background_color": "black",
"theme_color": "black"
}
This is enough for now. Here is a description of all the properties of this file, as will be the time, read necessarily.
Plug-in manifest.json in index.html in the <head>…</head> tag
<link rel="manifest" href="/manifest.json">
Let’s start writing scripts. Creating a folder named js, where we add the main.js file with this code:
window.addEventListener('load', () => {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(reg
=> {
console.log('SW registered!');
}).catch(err => console.log('SW registration
FAIL:', err));
}
});
A few words about what is happening there:
Enabling scripts in index.html, but no longer in the <head>…</head> tag, but before the closing </body> tag.
<script src="js/main.js"></script>
Well, now let’s create the file itself sw.js. It will store all the logic for Service Worker. Create it in the root of the project, and add the cache name as the first line.
const cacheName = 'pwa_v1';
Next line, add the includeToCache variable. In it, we specify the files to be cached. Yes, I understand that it is not convenient, I have to register all the files with my hands, but we have what we have. But we will always be sure that nothing extra is cached. Traffic savings and stability.
const includeToCache = [
'/',
'/index.html',
'/images/favicon.ico',
'/images/logo.svg',
'/images/logo-black.svg',
'/css/styles.css',
'/js/main.js'
];
Following we go to the event. Service Worker has several events under the hood, also called life cycles. And the first of them is install. It is triggered only once when writing the cache.
/* Start the service worker and cache all of the
app's content */
self.addEventListener('install', e => {
e.waitUntil(
caches.open(cacheName).then(cache => {
return cache.addAll(includeToCache);
})
);
});
Fetch event. This event scans all requests, and if something matches what is in the cache, it returns a match from the cache. Otherwise, it returns what comes from the server.
The respondWith method is responsible for retrieving data from the cache or the data returned by the server. And if the server didn’t return anything, we take it from the cache.
/* Serve cached content when offline */
self.addEventListener(‘fetch’, e => {
e.respondWith(
caches.match(e.request).then(response => {
return response || fetch(e.request);
})
);
});
This code is sufficient for now. Let’s now make sure that the file sw.js registered and the cache is recorded. Go to the developer console, open the Application tab, and then go to the Service Workers settings. Here we see that the file sw.js successfully registered, this is confirmed by a green light.
We continue to move along the side navigation panel, find a drop-down list with the name Cache Storage, where our cache is actually stored. If you click on it, you can see which files and content were cached.
Now, if you disable the Internet and reload the page, the site will work.
Sum up. In order to make the site work when there is no Internet, you do not need to install any frameworks, add libraries, and so on. A few lines of code and a General understanding of this technology are enough.
The first problem I encountered while dealing with PWA was updating the old cache. But, as it turned out, this is very easy to solve.
Let’s change a couple of styles so that you can see that something has changed. Then refresh the page and make sure that the styles have changed on the page. We cut off the Internet connection and reload the page again, but for some reason, the cache was not updated, and we see the old version of the site.
The solution is to add the activate event to sw.js file, when called, we will check the name of the old and new cache, and if the names differ, then delete the old one and add a new one. Yes, in order for the cache to be updated, we need to change its name every time the code is updated.
In the beginning, I did not specify the postfix *_v1 in the cache name for nothing, this will be its version. It doesn’t really matter what you call it, as long as the names are different.
self.addEventListener(‘activate’, e => {
// delete any caches that aren’t in cacheName
// which will get rid of version
e.waitUntil(
caches.keys().then(keys => Promise.all(
keys.map(key => {
if (cacheName !== key) {
return caches.delete(key);
}
})
)).then(() => {
console.log(cacheName + ‘ now ready to handle
fetches!’);
})
);
});
If you read the code, you can see a condition where the cache names are compared, and if they don’t match, the old cache is deleted.
A few words about the activate event. This event is triggered after the worker has been registered and is ready to work. But in order for it to be ready, you need to wait until the old cache is no longer used by the site, and this will take some time. And to get rid of this expectation, you can add the method below.
self.skipWaiting();
Now the cache will be updated immediately after registering a new worker. Adding it to the install event.
/* Start the service worker and cache all of the
app's content */
self.addEventListener('install', e => {
self.skipWaiting();
e.waitUntil(
caches.open(cacheName).then(cache => {
return cache.addAll(includeToCache);
})
);
});
And in the main.js file adding the update function, which will start updating the cache every time the page is reloaded.
reg.update();
Add the console.log() method. In fact, it does not matter, the main thing is that in the callback .then()
window.addEventListener('load', () => {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(reg
=> {
reg.update();
console.log('SW registered!');
}).catch(err => console.log('SW registration
FAIL:', err));
}
});
That’s it, reload the page. Open the developer tools again, check offline in the Service Workers tab on the side panel, reload the page again, and watch the Cache Storage tab. Here you can see how the old cache will be replaced by the new one.
Installing a new worker takes ~2 minutes, so you may need to reload the page several times.
And after the page, we see our new styles and updated cache. Hooray!
In this article, we’ve learned basic information about how to create and set up PWA technology in a real example. Please, feel free to comment on the article and share your experience about setting up the PWA.
Previously published at https://blog.maddevs.io/progressive-web-apps-practical-usage-guide-8f9ddfea1cb3