Πώς να δημιουργήσετε ένα PWA από το μηδέν με HTML, CSS και JavaScript

Οι προοδευτικές εφαρμογές ιστού είναι ένας τρόπος για να προσφέρετε αυτό το εγγενές συναίσθημα σε μια παραδοσιακή εφαρμογή ιστού. Με τα PWA μπορούμε να βελτιώσουμε τον ιστότοπό μας με δυνατότητες εφαρμογών για κινητά που αυξάνουν τη χρηστικότητα και προσφέρουν μια υπέροχη εμπειρία χρήστη.

Σε αυτό το άρθρο, θα δημιουργήσουμε ένα PWA από το μηδέν με HTML, CSS και JavaScript. Εδώ είναι τα θέματα που θα καλύψουμε:

  • Τι είναι μια Προοδευτική εφαρμογή Ιστού;
  • Σήμανση
  • Στυλ
  • Εμφάνιση δεδομένων με JavaScript
  • Εκδήλωση εφαρμογής ιστού
  • Τι είναι το Worker Service;
  • Κρυφή μνήμη των στοιχείων
  • Λήψη στοιχείων
  • Καταχωρήστε το Service Worker
  • Τελικές σκέψεις
  • Επόμενα βήματα

Ας ξεκινήσουμε λοιπόν με μια σημαντική ερώτηση: Τι είναι το PWA;

Τι είναι μια Προοδευτική εφαρμογή Ιστού;

Η Προοδευτική Εφαρμογή Ιστού είναι μια εφαρμογή ιστού που προσφέρει μια εμπειρία όπως η εφαρμογή στους χρήστες χρησιμοποιώντας σύγχρονες δυνατότητες ιστού. Στο τέλος, είναι απλώς ο κανονικός ιστότοπός σας που λειτουργεί σε ένα πρόγραμμα περιήγησης με ορισμένες βελτιώσεις. Σας δίνει τη δυνατότητα:

  • Για να το εγκαταστήσετε σε μια αρχική οθόνη για κινητά
  • Για πρόσβαση σε αυτό εκτός σύνδεσης
  • Για πρόσβαση στην κάμερα
  • Για να λαμβάνετε ειδοποιήσεις push
  • Για να κάνετε συγχρονισμό στο παρασκήνιο

Και πολλά άλλα.

Ωστόσο, για να μπορέσουμε να μετατρέψουμε την παραδοσιακή μας εφαρμογή ιστού σε PWA, πρέπει να την προσαρμόσουμε λίγο προσθέτοντας ένα αρχείο δήλωσης εφαρμογής ιστού και ένα service service.

Μην ανησυχείτε για αυτούς τους νέους όρους - θα τους καλύψουμε παρακάτω.

Πρώτον, πρέπει να δημιουργήσουμε την παραδοσιακή μας εφαρμογή ιστού. Ας ξεκινήσουμε λοιπόν με τη σήμανση.

Σήμανση

Το αρχείο HTML είναι σχετικά απλό. Τυλίγουμε τα πάντα στην mainετικέτα.

  • Σε index.html
       Dev'Coffee PWA     

Dev'Coffee

  • Home
  • About
  • Blog

Και δημιουργήστε μια γραμμή πλοήγησης με την navετικέτα. Στη συνέχεια, το divμε το μάθημα .containerθα κρατήσει τις κάρτες μας που προσθέτουμε αργότερα με JavaScript.

Τώρα που το έχουμε ξεπεράσει, ας το στυλ με CSS

Στυλ

Εδώ, ως συνήθως, ξεκινάμε εισάγοντας τις γραμματοσειρές που χρειαζόμαστε. Τότε θα κάνουμε κάποιες επαναφορές για να αποτρέψουμε την προεπιλεγμένη συμπεριφορά.

  • Σε css/style.css
@import url("//fonts.googleapis.com/css?family=Nunito:400,700&display=swap"); * { margin: 0; padding: 0; box-sizing: border-box; } body { background: #fdfdfd; font-family: "Nunito", sans-serif; font-size: 1rem; } main { max-width: 900px; margin: auto; padding: 0.5rem; text-align: center; } nav { display: flex; justify-content: space-between; align-items: center; } ul { list-style: none; display: flex; } li { margin-right: 1rem; } h1 { color: #e74c3c; margin-bottom: 0.5rem; } 

Στη συνέχεια, περιορίζουμε το mainμέγιστο πλάτος του στοιχείου 900pxώστε να φαίνεται καλό σε μια μεγάλη οθόνη.

Για το navbar, θέλω το λογότυπο να βρίσκεται στα αριστερά και οι σύνδεσμοι στα δεξιά. Έτσι, για την navετικέτα, αφού την κάναμε ένα ευέλικτο δοχείο, χρησιμοποιούμε justify-content: space-between;για να τα ευθυγραμμίσουμε.

  • Σε css/style.css
.container { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); grid-gap: 1rem; justify-content: center; align-items: center; margin: auto; padding: 1rem 0; } .card { display: flex; align-items: center; flex-direction: column; width: 15rem auto; height: 15rem; background: #fff; box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23); border-radius: 10px; margin: auto; overflow: hidden; } .card--avatar { width: 100%; height: 10rem; object-fit: cover; } .card--title { color: #222; font-weight: 700; text-transform: capitalize; font-size: 1.1rem; margin-top: 0.5rem; } .card--link { text-decoration: none; background: #db4938; color: #fff; padding: 0.3rem 1rem; border-radius: 20px; } 

Θα έχουμε πολλές κάρτες, οπότε για το στοιχείο κοντέινερ θα εμφανίζεται ως πλέγμα. Και, με grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)), μπορούμε τώρα να κάνουμε τις κάρτες μας ανταποκρινόμενες έτσι ώστε να χρησιμοποιούν τουλάχιστον 15remπλάτος εάν υπάρχει αρκετός χώρος (και 1frαν όχι).

Και για να φαίνονται ωραία, διπλασιάζουμε το εφέ σκιάς στην .cardτάξη και το χρησιμοποιούμε object-fit: coverγια .card--avatarνα αποτρέψουμε το τέντωμα της εικόνας.

Τώρα φαίνεται πολύ καλύτερο - αλλά ακόμα δεν έχουμε δεδομένα για εμφάνιση.

Ας το διορθώσουμε στην επόμενη ενότητα

Εμφάνιση δεδομένων με JavaScript

Παρατηρήστε ότι χρησιμοποίησα μεγάλες εικόνες που χρειάζονται λίγο χρόνο για τη φόρτωση. Αυτό θα σας δείξει με τον καλύτερο τρόπο τη δύναμη των εργαζομένων.

Όπως είπα νωρίτερα, το .containerμάθημα θα κρατήσει τις κάρτες μας. Επομένως, πρέπει να το επιλέξουμε.

  • Σε js/app.js
const container = document.querySelector(".container") const coffees = [ { name: "Perspiciatis", image: "images/coffee1.jpg" }, { name: "Voluptatem", image: "images/coffee2.jpg" }, { name: "Explicabo", image: "images/coffee3.jpg" }, { name: "Rchitecto", image: "images/coffee4.jpg" }, { name: " Beatae", image: "images/coffee5.jpg" }, { name: " Vitae", image: "images/coffee6.jpg" }, { name: "Inventore", image: "images/coffee7.jpg" }, { name: "Veritatis", image: "images/coffee8.jpg" }, { name: "Accusantium", image: "images/coffee9.jpg" }, ] 

Στη συνέχεια, δημιουργούμε μια σειρά καρτών με ονόματα και εικόνες.

  • Σε js/app.js
const showCoffees = () => { let output = "" coffees.forEach( ({ name, image }) => (output += ` 

${name}

Taste `) ) container.innerHTML = output } document.addEventListener("DOMContentLoaded", showCoffees)

With this code above, we can now loop through the array and show them on the HTML file. And to make everything work, we wait until the DOM (Document Object Model) content finishes loading to run the showCoffees method.

We've done a lot, but for now, we just have a traditional web app. So, let's change that in the next section by introducing some PWA features.

πολύ ενθουσιασμένος

Web App Manifest

The web app manifest is a simple JSON file that informs the browser about your web app. It tells how it should behave when installed on the user's mobile device or desktop. And to show the Add to Home Screen prompt, the web app manifest is required.

Now that we know what a web manifest is, let's create a new file named manifest.json (you have to name it like that) in the root directory. Then add this code block below.

  • In manifest.json
{ "name": "Dev'Coffee", "short_name": "DevCoffee", "start_url": "index.html", "display": "standalone", "background_color": "#fdfdfd", "theme_color": "#db4938", "orientation": "portrait-primary", "icons": [ { "src": "/images/icons/icon-72x72.png", "type": "image/png", "sizes": "72x72" }, { "src": "/images/icons/icon-96x96.png", "type": "image/png", "sizes": "96x96" }, { "src": "/images/icons/icon-128x128.png", "type": "image/png","sizes": "128x128" }, { "src": "/images/icons/icon-144x144.png", "type": "image/png", "sizes": "144x144" }, { "src": "/images/icons/icon-152x152.png", "type": "image/png", "sizes": "152x152" }, { "src": "/images/icons/icon-192x192.png", "type": "image/png", "sizes": "192x192" }, { "src": "/images/icons/icon-384x384.png", "type": "image/png", "sizes": "384x384" }, { "src": "/images/icons/icon-512x512.png", "type": "image/png", "sizes": "512x512" } ] } 

In the end, it's just a JSON file with some mandatory and optional properties.

name: When the browser launches the splash screen, it will be the name displayed on the screen.

short_name: It will be the name displayed underneath your app shortcut on the home screen.

start_url: It will be the page shown to the user when your app is open.

display: It tells the browser how to display the app. There are several modes like minimal-ui, fullscreen, browser etc. Here, we use the standalone mode to hide everything related to the browser.

background_color: When the browser launches the splash screen, it will be the background of the screen.

theme_color: It will be the background color of the status bar when we open the app.

orientation: It tells the browser the orientation to have when displaying the app.

icons: When the browser launches the splash screen, it will be the icon displayed on the screen. Here, I used all sizes to fit any device's preferred icon. But you can just use one or two. It's up to you.

Now that we have a web app manifest, let's add it to the HTML file.

  • In index.html (head tag)

As you can see, we linked our manifest.json file to the head tag. And add some other links which handle the iOS support to show the icons and colorize the status bar with our theme color.

With that, we can now dive into the final part and introduce the service worker.

What is a Service Worker?

Notice that PWAs run only on https because the service worker can access the request and handle it. Therefore security is required.

A service worker is a script that your browser runs in the background in a separate thread. That means it runs in a different place and is completely separate from your web page. That's the reason why it can't manipulate your DOM element.

However, it's super powerful. The service worker can intercept and handle network requests, manage the cache to enable offline support or send push notifications to your users.

Ουάου

S0 let's create our very first service worker in the root folder and name it serviceWorker.js (the name is up to you). But you have to put it in the root so that you don't limit its scope to one folder.

Cache the assets

  • In serviceWorker.js
const staticDevCoffee = "dev-coffee-site-v1" const assets = [ "/", "/index.html", "/css/style.css", "/js/app.js", "/images/coffee1.jpg", "/images/coffee2.jpg", "/images/coffee3.jpg", "/images/coffee4.jpg", "/images/coffee5.jpg", "/images/coffee6.jpg", "/images/coffee7.jpg", "/images/coffee8.jpg", "/images/coffee9.jpg", ] self.addEventListener("install", installEvent => { installEvent.waitUntil( caches.open(staticDevCoffee).then(cache => { cache.addAll(assets) }) ) }) 

This code looks intimidating first but it just JavaScript (so don't worry).

We declare the name of our cache staticDevCoffee and the assets to store in the cache. And to perform that action, we need to attach a listener to self.

self is the service worker itself. It enables us to listen to life cycle events and do something in return.

The service worker has several life cycles, and one of them is the install event. It runs when a service worker is installed. It's triggered as soon as the worker executes, and it's only called once per service worker.

When the install event is fired, we run the callback which gives us access to the event object.

Caching something on the browser can take some time to finish because it's asynchronous.

So to handle it, we need to use waitUntil() which, as you might guess, waits for the action to finish.

Once the cache API is ready, we can run the open() method and create our cache by passing its name as an argument to caches.open(staticDevCoffee).

Then it returns a promise, which helps us store our assets in the cache with cache.addAll(assets).

προσωρινή μνήμη εικόνας

Hopefully, you're still with me.

απελπισμένος

Now, we've successfully cached our assets in the browser. And the next time we load the page, the service worker will handle the request and fetch the cache if we are offline.

So, let's fetch our cache.

Fetch the assets

  • In serviceWorker.js
self.addEventListener("fetch", fetchEvent => { fetchEvent.respondWith( caches.match(fetchEvent.request).then(res =>  return res ) ) }) 

Here, we use the fetch event to, well, get back our data. The callback gives us access to fetchEvent. Then we attach respondWith() to prevent the browser's default response. Instead it returns a promise because the fetch action can take time to finish.

And once the cache ready, we apply the caches.match(fetchEvent.request). It will check if something in the cache matches fetchEvent.request. By the way, fetchEvent.request is just our array of assets.

Then, it returns a promise. And finally, we can return the result if it exists or the initial fetch if not.

Now, our assets can be cached and fetched by the service worker which increases the load time of our images quite a bit.

And most important, it makes our app available in offline mode.

But a service worker alone can't do the job. We need to register it in our project.

ας το κάνουμε

Register the Service Worker

  • In js/app.js
if ("serviceWorker" in navigator) { window.addEventListener("load", function() { navigator.serviceWorker .register("/serviceWorker.js") .then(res => console.log("service worker registered")) .catch(err => console.log("service worker not registered", err)) }) } 

Here, we start by checking if the serviceWorker is supported by the current browser (as it's still not supported by all browsers).

Then, we listen to the page load event to register our service worker by passing the name of our file serviceWorker.js to navigator.serviceWorker.register() as a parameter to register our worker.

With this update, we have now transformed our regular web app to a PWA.

τα καταφέραμε

Final thoughts

Throughout this article, we have seen how amazing PWAs can be. By adding a web app manifest file and a service worker, it really improves the user experience of our traditional web app. This is because PWAs are fast, secure, reliable, and – most importantly – they support offline mode.

Many frameworks out there now come with a service worker file already set-up for us. But knowing how to implement it with Vanilla JavaScript can help you understand PWAs.

And you can go even further with service workers by caching assets dynamically or limiting the size of your cache and so on.

Ευχαριστούμε που διαβάσατε αυτό το άρθρο.

Μπορείτε να το δείτε ζωντανά εδώ και ο πηγαίος κώδικας είναι εδώ.

Διαβάστε περισσότερα από τα άρθρα μου στο ιστολόγιό μου

Επόμενα βήματα

Τεκμηρίωση μανιφέστο Ιστού

Τεκμηρίωση εργαζομένων σέρβις

Δημιουργία εκδήλωσης Ιστού

Υποστήριξη προγράμματος περιήγησης