Aplicațiile web progresive sunt o modalitate de a aduce sentimentul aplicației native într-o aplicație web tradițională. Cu PWA-uri putem îmbunătăți site-ul nostru web cu funcții de aplicații mobile care sporesc gradul de utilizare și oferă o experiență excelentă pentru utilizator.

În acest articol, vom construi un PWA de la zero cu HTML, CSS și JavaScript. Iată subiectele pe care le vom aborda:

  • Ce este o aplicație web progresivă?
  • Markup
  • Styling
  • Afișați date cu JavaScript
  • Manifestul aplicației web
  • Ce este un lucrător de service?
  • Memorează în cache activele
  • Aduceți activele
  • Înregistrați lucrătorul de service
  • Gânduri finale
  • Pasii urmatori

Deci, să începem cu o întrebare importantă: Ce naiba este un PWA?

Ce este o aplicație web progresivă?

O aplicație web progresivă este o aplicație web care oferă utilizatorilor o experiență asemănătoare aplicației prin utilizarea funcțiilor web moderne. În cele din urmă, este doar site-ul dvs. obișnuit care rulează într-un browser cu unele îmbunătățiri. Vă oferă abilitatea:

  • Pentru a-l instala pe un ecran de pornire mobil
  • Pentru a-l accesa offline
  • Pentru a accesa camera
  • Pentru a primi notificări push
  • Pentru a face sincronizarea de fundal

Și multe altele.

Cu toate acestea, pentru a putea transforma aplicația noastră tradițională web într-un PWA, trebuie să o ajustăm puțin adăugând un fișier manifest al aplicației web și un lucrător de service.

Nu vă faceți griji cu privire la acești noi termeni – îi vom acoperi mai jos.

În primul rând, trebuie să construim aplicația noastră tradițională web. Deci, să începem cu marcajul.

Markup

Fișierul HTML este relativ simplu. Înfășurăm totul în main etichetă.

  • În index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <link rel="stylesheet" href="https://www.freecodecamp.org/news/build-a-pwa-from-scratch-with-html-css-and-javascript/css/style.css" />
    <title>Dev'Coffee PWA</title>
  </head>
  <body>
    <main>
      <nav>
        <h1>Dev'Coffee</h1>
        <ul>
          <li>Home</li>
          <li>About</li>
          <li>Blog</li>
        </ul>
      </nav>
      <div class="container"></div>
    </main>
    <script src="js/app.js"></script>
  </body>
</html>

Și creați o bară de navigare cu nav etichetă. Apoi, div cu clasa .container ne va păstra cardurile pe care le adăugăm mai târziu cu JavaScript.

Acum, că am scăpat de asta, să o stilăm cu CSS.

Styling

Aici, ca de obicei, începem prin importarea fonturilor de care avem nevoie. Apoi vom face câteva resetări pentru a preveni comportamentul implicit.

  • În css/style.css
@import url("https://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;
}

Apoi, limităm main lățimea maximă a elementului la 900px pentru a face să arate bine pe un ecran mare.

Pentru bara de navigare, vreau ca sigla să fie în stânga și linkurile în dreapta. Deci pentru nav etichetă, după ce îl facem un container flexibil, îl folosim justify-content: space-between; pentru a le alinia.

  • În 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;
}

Vom avea mai multe carduri, deci pentru elementul container va fi afișat ca o grilă. Si cu grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)), acum putem face cărțile noastre receptive, astfel încât să le folosească cel puțin 15rem lățime dacă există suficient spațiu (și 1fr dacă nu).

Și pentru a le face să arate frumos, dublăm efectul de umbră asupra .card clasă și utilizare object-fit: cover pe .card--avatar pentru a preveni întinderea imaginii.

Acum arată mult mai bine – dar încă nu avem date de afișat.

Să o remediem în secțiunea următoare

Afișați date cu JavaScript

Observați că am folosit imagini mari, care necesită ceva timp pentru încărcare. Acest lucru vă va arăta în cel mai bun mod puterea lucrătorilor din servicii.

După cum am spus mai devreme, .container clasa ne va ține cărțile. Prin urmare, trebuie să o selectăm.

  • În 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" },
]

Apoi, creăm o serie de cărți cu nume și imagini.

  • În js/app.js
const showCoffees = () => {
  let output = ""
  coffees.forEach(
    ({ name, image }) =>
      (output += `
              <div class="card">
                <img class="card--avatar" src=${image} />
                <h1 class="card--title">${name}</h1>
                <a class="card--link" href="https://www.freecodecamp.org/news/build-a-pwa-from-scratch-with-html-css-and-javascript/#">Taste</a>
              </div>
              `)
  )
  container.innerHTML = output
}

document.addEventListener("DOMContentLoaded", showCoffees)

Cu acest cod de mai sus, acum putem parcurge matricea și le putem arăta în fișierul HTML. Și pentru ca totul să funcționeze, așteptăm până când conținutul DOM (Document Object Model) se termină de încărcat pentru a rula showCoffees metodă.

Am făcut multe, dar deocamdată avem doar o aplicație web tradițională. Deci, să schimbăm acest lucru în secțiunea următoare prin introducerea unor caracteristici PWA.

foarte încântat

Manifestul aplicației web

Manifestul aplicației web este un fișier JSON simplu care informează browserul despre aplicația dvs. web. Acesta spune cum ar trebui să se comporte atunci când este instalat pe dispozitivul mobil sau pe desktopul utilizatorului. Și pentru a afișa solicitarea Adăugați la ecranul de pornire, este necesar manifestul aplicației web.

Acum, că știm ce este un manifest web, să creăm un nou fișier numit manifest.json (trebuie să-l numiți așa) în directorul rădăcină. Apoi adăugați acest bloc de cod mai jos.

  • În 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"
    }
  ]
}

În cele din urmă, este doar un fișier JSON cu câteva proprietăți obligatorii și opționale.

nume: Când browserul lansează splash screen, acesta va fi numele afișat pe ecran.

short_name: Acesta va fi numele afișat sub comanda rapidă a aplicației pe ecranul de pornire.

start_url: Va fi pagina afișată utilizatorului când aplicația dvs. este deschisă.

afișare: îi spune browserului cum să afișeze aplicația. Există mai multe moduri cum ar fi minimal-ui, fullscreen, browser etc. Aici, folosim standalone pentru a ascunde tot ceea ce este legat de browser.

background_color: Când browserul lansează splash screen, acesta va fi fundalul ecranului.

theme_color: Va fi culoarea de fundal a barei de stare atunci când vom deschide aplicația.

orientare: îi spune browserului orientarea pe care trebuie să o aibă atunci când afișează aplicația.

pictograme: Când browserul lansează splash screen, va fi pictograma afișată pe ecran. Aici, am folosit toate dimensiunile pentru a se potrivi cu pictograma preferată a oricărui dispozitiv. Dar puteți folosi doar una sau două. Depinde de tine.

Acum că avem un manifest de aplicație web, să îl adăugăm în fișierul HTML.

  • În index.html (eticheta capului)
<link rel="manifest" href="https://www.freecodecamp.org/news/build-a-pwa-from-scratch-with-html-css-and-javascript/manifest.json" />
<!-- ios support -->
<link rel="apple-touch-icon" href="images/icons/icon-72x72.png" />
<link rel="apple-touch-icon" href="images/icons/icon-96x96.png" />
<link rel="apple-touch-icon" href="images/icons/icon-128x128.png" />
<link rel="apple-touch-icon" href="images/icons/icon-144x144.png" />
<link rel="apple-touch-icon" href="images/icons/icon-152x152.png" />
<link rel="apple-touch-icon" href="images/icons/icon-192x192.png" />
<link rel="apple-touch-icon" href="images/icons/icon-384x384.png" />
<link rel="apple-touch-icon" href="images/icons/icon-512x512.png" />
<meta name="apple-mobile-web-app-status-bar" content="#db4938" />
<meta name="theme-color" content="#db4938" />

După cum puteți vedea, ne-am legat de manifest.json fișier la eticheta de cap. Și adăugați alte linkuri care gestionează suportul iOS pentru a afișa pictogramele și a colora bara de stare cu culoarea temei noastre.

Cu aceasta, putem acum să ne scufundăm în partea finală și să îl prezentăm pe lucrătorul de service.

Ce este un lucrător de service?

Observați că PWA rulează numai pe https, deoarece lucrătorul de service poate accesa cererea și o poate gestiona. Prin urmare, este necesară securitatea.

Un lucrător de service este un script pe care browserul dvs. îl rulează în fundal într-un fir separat. Asta înseamnă că rulează într-un alt loc și este complet separat de pagina dvs. web. Acesta este motivul pentru care nu poate manipula elementul DOM.

Cu toate acestea, este super puternic. Lucrătorul de servicii poate intercepta și gestiona cererile de rețea, gestiona memoria cache pentru a permite asistență offline sau trimite notificări push utilizatorilor dvs.

Wow

S0 să creăm primul nostru lucrător de service în folderul rădăcină și să-l numim serviceWorker.js (numele depinde de tine). Dar trebuie să-l puneți în rădăcină, astfel încât să nu limitați domeniul său de aplicare la un singur folder.

Memorează în cache activele

  • În 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)
    })
  )
})

Acest cod arată mai întâi intimidant, ci doar JavaScript (deci nu vă faceți griji).

Declarăm numele cache-ului nostru staticDevCoffee și activele de stocat în cache. Și pentru a efectua acțiunea respectivă, trebuie să atașăm un ascultător self.

self este lucrătorul de servicii în sine. Ne permite să ascultăm evenimente din ciclul vieții și să facem ceva în schimb.

Lucrătorul de servicii are mai multe cicluri de viață, iar unul dintre ele este install eveniment. Se execută când este instalat un lucrător de service. Este declanșat de îndată ce lucrătorul execută și este apelat o singură dată pentru fiecare lucrător de service.

Cand install evenimentul este declanșat, rulăm callback-ul care ne oferă acces la event obiect.

Memorarea în cache a ceva în browser poate dura ceva timp pentru a finaliza, deoarece este asincronă.

Deci, pentru a face față, trebuie să folosim waitUntil() care, după cum ați putea ghici, așteaptă finalizarea acțiunii.

Odată ce API-ul cache este gata, putem rula open() metoda și creați memoria cache trecându-i numele ca argument către caches.open(staticDevCoffee).

Apoi, returnează o promisiune, care ne ajută să ne stocăm activele în cache cu cache.addAll(assets).

imagine-cache

Sperăm că ești încă cu mine.

deznădăjduit

Acum, ne-am memorat cu succes activele în browser. Și data viitoare când încărcăm pagina, lucrătorul de service va gestiona cererea și va prelua memoria cache dacă suntem offline.

Deci, să ne aducem memoria cache.

Aduceți activele

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

Aici, folosim fetch eveniment pentru a ne recupera datele. Apelarea ne oferă acces la fetchEvent. Apoi ne atașăm respondWith() pentru a preveni răspunsul implicit al browserului. În schimb, returnează o promisiune, deoarece acțiunea de preluare poate dura mult timp pentru a finaliza.

Și odată ce memoria cache este gata, aplicăm caches.match(fetchEvent.request). Se va verifica dacă ceva din cache se potrivește fetchEvent.request. Apropo, fetchEvent.request este doar gama noastră de active.

Apoi, returnează o promisiune. Și, în cele din urmă, putem returna rezultatul dacă există sau preluarea inițială dacă nu.

Acum, activele noastre pot fi stocate în cache și preluate de către lucrătorul de service, ceea ce crește destul de mult timpul de încărcare a imaginilor noastre.

Și cel mai important, face aplicația noastră disponibilă în modul offline.

Dar un muncitor de servicii singur nu poate face treaba. Trebuie să-l înregistrăm în proiectul nostru.

s-o facem

Înregistrați lucrătorul de service

  • În 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))
  })
}

Aici, începem prin a verifica dacă serviceWorker este acceptat de browserul curent (deoarece nu este încă acceptat de toate browserele).

Apoi, ascultăm evenimentul de încărcare a paginii pentru a înregistra lucrătorul nostru de service, trecând numele fișierului nostru serviceWorker.js la navigator.serviceWorker.register() ca parametru pentru înregistrarea lucrătorului nostru.

Cu această actualizare, am transformat acum aplicația noastră web obișnuită într-un PWA.

am reusit

Gânduri finale

De-a lungul acestui articol, am văzut cât de uimitoare pot fi PWA-urile. Prin adăugarea unui fișier manifest al aplicației web și a unui lucrător de service, aceasta îmbunătățește cu adevărat experiența utilizatorului aplicației noastre tradiționale web. Acest lucru se datorează faptului că PWA-urile sunt rapide, sigure, fiabile și – cel mai important – acceptă modul offline.

Multe cadre de pe piață vin acum cu un fișier de service service deja configurat pentru noi. Dar să știi cum să-l implementezi cu Vanilla JavaScript te poate ajuta să înțelegi PWA-urile.

Și puteți merge chiar mai departe cu lucrătorii de service prin stocarea în cache a activelor dinamic sau prin limitarea dimensiunii cache-ului dvs. și așa mai departe.

Vă mulțumim că ați citit acest articol.

O puteți verifica live Aici iar codul sursă este Aici.

Citiți mai multe articole despre blogul meu

Pasii urmatori

Documentare manifest web

Documentația pentru lucrătorii de service

Generator de manifest web

Suport pentru browser