"Hors ligne d'abord" est un paradigme de développement d'applications dans lequel les développeurs s'assurent que la fonctionnalité d'une application n'est pas affectée par une perte temporaire de connectivité réseau. Les applications Web progressives, qui ressemblent à des applications natives mais fonctionnent comme des applications Web, sont souvent construites sur ce paradigme.
Ce didacticiel vous apprendra à créer une première application hors ligne avec Node.js et une base de données SQLite. Commençons par une introduction aux applications Web progressives.
Présentation des PWA
Les applications Web progressives (PWA) sont des applications Web qui utilisent des agents de service, des manifestes et d'autres fonctionnalités de plate-forme Web et une amélioration progressive pour offrir aux utilisateurs une expérience comparable aux applications natives.
Les PWA peuvent parfois surpasser les applications natives en termes d'efficacité. Ils fonctionnent à la demande et sont toujours disponibles sans consommer de mémoire ou de données précieuses sur les smartphones. Les utilisateurs consomment moins de données lorsqu'ils choisissent une PWA plutôt qu'une version native de la même application. Ils peuvent toujours enregistrer la PWA sur leur écran d'accueil ; il est installable sans avoir besoin d'un téléchargement complet.
Que construisons-nous ?
Pour démontrer la puissance des applications Web progressives, nous allons créer une simple application de blog.
L'utilisateur pourra interagir avec elle comme d'autres PWA, comme la PWA de Twitter. Allons droit au but.
Initialiser l'application NodeJs
Mettons nos mains dans le cambouis. Pour commencer, nous allons créer notre dossier de projet avec la commande ci-dessous :
mkdir PWA && cd PWA
Ensuite, nous allons initialiser une application Node.js avec les commandes ci-dessous :
npm init -y
La commande ci-dessus crée un package.json
fichier pour l'application.
Ensuite, créez la structure de dossiers suivante dans notre dossier de projet :
Configurer un serveur Express
Avec notre configuration d'application, installons Express pour créer notre serveur Node.js avec la commande ci-dessous :
npm install express
Ensuite, nous allons créer quelques dossiers et fichiers dans le dossier public :
- fichier css/style.css
- fichier js/app.js
Ensuite, créez un index.js
fichier dans le répertoire racine du projet avec les extraits de code suivants ci-dessous :
const express = require("express");
const path = require("path");
const app = express();
app.use(express.static(path.join(__dirname, "public")));
app.get("/", function (req, res) {
res.sendFile(path.join(__dirname, "public/index.html"));
});
app.listen(8000, () => console.log("Server is running on Port 8000"));
Dans l'extrait de code, nous importons express pour créer notre serveur et le chemin module. Nous avons configuré notre application pour afficher nos fichiers statiques à l'aide de express.static méthode, qui prend le chemin vers le dossier statique (public), nous avons créé la route racine de notre application et rendu le index.html dossier. Ensuite, nous avons configuré l'application pour écouter le port 8000 .
Se connecter à la base de données SQLite
Avec la configuration du serveur pour notre application, créons et connectons notre application pour enregistrer les détails de notre blog. Pour commencer, exécutez la commande ci-dessous pour installer la dépendance sqlite3.
npm install sqlite3
Puis, dans le point d'entrée index.js
fichier, ajoutez l'extrait de code ci-dessous pour créer et connecter l'application à une base de données SQLite.
const db = new sqlite3.Database("db.sqlite", (err) => {
if (err) {
// Cannot open database
console.error(err.message);
throw err;
} else {
console.log("Connected to the SQLite database.");
}
});
Ensuite, nous allons créer une liste de blogs que nous allons stocker dans notre base de données et rendre plus tard côté client avec l'extrait de code ci-dessous :
let blogs = [
{
id: "1",
title: "How To Build A RESTAPI With Javascript",
avatar: "images/coffee2.jpg",
intro: "iste odio beatae voluptas dolor praesentium illo facere optio nobis magni, aspernatur quas.",
},
{
id: "2",
title: "How to Build an Offline-First Application with Node.js,"
avatar: "images/coffee2.jpg",
"iste odio beatae voluptas dolor praesentium illo facere optio nobis magni, aspernatur quas.",
},
{
id: "3",
title: "Building a Trello Clone with React DnD",
avatar: "images/coffee2.jpg",
intro: "iste odio beatae voluptas dolor praesentium illo facere optio nobis magni, aspernatur quas.",
},
];
Chaque publication bloquée dans notre application aura un id , titre , avatar , et introduction des champs.
Créez maintenant un nom de table de base de données blogs et enregistrez les détails du blog que nous venons de créer ci-dessus avec l'extrait de code ci-dessous :
db.run(
`CREATE TABLE blog (id INTEGER PRIMARY KEY AUTOINCREMENT, title text,avatar text,intro text)`,
(err) => {
if (err) {
// console.log(err)
// Table already created
} else {
// Table just created, creating some rows
var insert = "INSERT INTO blogs (title, avatar, intro) VALUES (?,?,?)";
blogs.map((blog) => {
db.run(insert, [
`${blog.title}`,
`${blog.avatar}`,
`${blog.intro}`,
]);
});
}
}
);
Dans l'extrait de code, nous avons créé un tableau blogs en utilisant db.run. Le db.run prend une requête SQL comme paramètre, puis nous parcourons notre tableau de blogs et les insérons dans la table des blogs que nous venons de créer à l'aide de la fonction js map.
Afficher les enregistrements de la base de données
Voyons maintenant les enregistrements que nous venons de créer à l'aide d'Arctype. Pour afficher les enregistrements de votre base de données SQLite à l'aide d'Arctype, suivez les étapes ci-dessous :
- Installer Arctype
- Exécuter l'application avec
node index.js
créer une base de données - Lancez Arctype et cliquez sur l'onglet SQLite
- Cliquez sur Sélectionner le fichier SQLite et localisez le db.sqlite fichier généré lorsque vous avez exécuté le serveur.
- Vous devriez voir le tableau des blogs et les enregistrements que nous créons comme indiqué dans la capture d'écran ci-dessous :
Afficher la page
À ce stade, nous avons connecté l'application à une base de données SQLite et également inséré des enregistrements dans la base de données. Maintenant, ouvrez le index.html fichier et ajoutez les extraits de code suivants ci-dessous :
<!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="css/style.css" />
<title>Blogger</title>
<link rel="manifest" href="manifest" />
</head>
<body>
<section>
<nav>
<h1>Blogger</h1>
<ul>
<li>Home</li>
<li class="active">Blog</li>
</ul>
</nav>
<div class="container"></div>
</section>
<script src="js/app.js"></script>
</body>
</html>
Nous avons créé un balisage simple avec des liens vers notre manifeste dans le fichier ci-dessus, que nous allons créer dans la section suivante, styles , et app.js des dossiers.
Ensuite, nous créerons un blogs route dans notre index.js fichier pour renvoyer les blogs côté client.
...
app.get("/blogs", (req, res) => {
res.status(200).json({
blogs,
});
});
...
Dans notre public/js/app.js fichier, nous enverrons une requête get au point de terminaison du blog pour obtenir les blogs de notre backend. Ensuite, nous parcourons les blogs, ciblons le conteneur classez et affichez-les.
let result = "";
fetch("http://localhost:8000/blogs")
.then((res) => res.json())
.then(({ rows } = data) => {
rows.forEach(({ title, avatar, intro } = rows) => {
result += `
<div class="card">
<img class="card-avatar" src="/${avatar}"/>
<h1 class="card-title">${title}</h1>
<p class="intro">${intro}</p>
<a class="card-link" href="#">Read</a>
</div>
`;
});
document.querySelector(".container").innerHTML = result;
})
.catch((e) => {
console.log(e);
});
Nous allons également ajouter du style à notre application dans le public/css/style.css avec le bout de code ci-dessous :
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #fdfdfd;
font-size: 1rem;
}
section {
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: #0e9c95;
margin-bottom: 0.5rem;
}
.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;
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: #16a0d6e7;
color: #fff;
padding: 0.3rem 1rem;
border-radius: 20px;
margin: 10px;
}
.intro {
color: #c2c5c5;
padding: 10px;
}
.active {
color: #16a0d6e7;
}
Ouvrez maintenant le package.json fichier et ajoutez le script de démarrage.
"start": "node index.js"
À ce stade, nous avons configuré notre application. Mais nous ne pouvons pas exécuter notre application lorsque le serveur ne fonctionne pas ou lorsqu'il n'y a pas de connexion réseau pour la production. Configurons cela dans la section suivante.
Optimisation de l'application
Nous devons rendre notre application compatible avec toutes les tailles d'écran. Nous ajouterons également une couleur de thème en ajoutant le balisage ci-dessous dans la section d'en-tête de notre index.html dossier.
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#16a0d6e7"/>
Créer un manifeste
Nous devons décrire notre application et comment elle doit se comporter lorsqu'elle est installée sur l'appareil de l'utilisateur. Nous pouvons le faire en créant un manifeste.
Créer un manifeste fichier dans le répertoire racine du projet et ajoutez les configurations suivantes :
{
"name": "Blogger"
"short_name": "Blogger"
"start_url": "/",
"display": "standalone",
"background_color": "#0e9c95",
"theme_color": "#16a0d6e7",
"orientation": "portrait",
"icons": []
}
Dans notre manifeste, nous avons défini les configurations suivantes :
- nom :Cela définit le nom d'affichage de l'application.
- short_name :Ceci définit le nom qui sera affiché sous l'icône de l'application lors de l'installation.
- start_url :Cela indique au navigateur l'URL racine de l'application.
- afficher :Cela indique au navigateur comment afficher l'application.
- couleur_fond : Ceci définit la couleur de fond de l'application lors de son installation.
- theme_color : Ceci définit la couleur de la barre d'état.
- orientation : Ceci définit l'orientation à utiliser lors de l'affichage de l'application.
- icônes : Cela définit les icônes ou les images de différentes tailles à utiliser comme icônes d'accueil de nos applications.
Créer manuellement nos icônes d'écran d'accueil peut être une tâche très compliquée, mais ne vous inquiétez pas. Nous profiterons d'un module tiers connu sous le nom de pwa-asset-generator pour générer des icônes de différentes tailles à partir de l'icône de notre application principale dans le répertoire public avec la commande ci-dessous :
#change directory to the public folder
cd public
#generate icons
npx pwa-asset-generator logo.png icons
La commande ci-dessus créera une icônes dossier à l'intérieur du dossier public avec de nombreuses icônes pour notre application, ainsi que du JSON sur le terminal que nous collerons dans notre tableau d'icônes dans le manifeste.
Le tableau d'icônes dans notre manifeste devrait ressembler à ceci :
"icons": [
{
"src": "public/icons/manifest-icon-192.maskable.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "public/icons/manifest-icon-192.maskable.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "public/icons/manifest-icon-512.maskable.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "public/icons/manifest-icon-512.maskable.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
De plus, la commande a généré les liens de balisage vers les icônes générées.
Copiez et collez le balisage dans la section d'en-tête du balisage dans public/index.html fichier.
Configurer les techniciens de service
Une fois notre manifeste créé, configurons les service workers. Un agent de service est un morceau de code JavaScript que votre navigateur exécute en arrière-plan dans un thread séparé pour gérer le cache des actifs et des données que vous enregistrez pour les demandes futures afin d'activer la prise en charge hors ligne de votre application.
Créez donc un blogger.serviceWorker.js fichier au public dossier. Pour le service worker, il existe de nombreux événements (pousser, activer, installer, récupérer, envoyer un message, synchroniser), mais pour la démonstration de ce didacticiel, nous couvrirons l'installation, activation, et récupérer événements. Avant cela, nous devons créer un tableau pour stocker tous les actifs que nous avons utilisés dans notre application.
const assets = [
"/",
"css/style.css",
"js/app.js",
"/images/blog1.jpg",
"/images/blog2.jpg",
"/images/blog3.jpg,"
];
Ensuite, nous écouterons l'installation événement pour enregistrer et enregistrer nos fichiers statiques dans le cache du navigateur. Ce processus prend un certain temps. Pour éviter l'attente, nous utiliserons skipWaiting().
const BLOGGER_ASSETS = "blogger-assets";
self.addEventListener("install", (installEvt) => {
installEvt.waitUntil(
caches
.open(BLOGGER_ASSETS)
.then((cache) => {
cache.addAll(assets);
})
.then(self.skipWaiting())
.catch((e) => {
console.log(e);
})
);
});
...
Ensuite, nous devons vider le cache pour supprimer les anciens actifs chaque fois que le service worker est mis à jour. Pour cela, nous allons écouter le activer extrait de code ci-dessous :
...
self.addEventListener("activate", function (evt) {
evt.waitUntil(
caches
.keys()
.then((keysList) => {
return Promise.all(
keysList.map((key) => {
if (key === BLOGGER_ASSETS) {
console.log(`Removed old cache from ${key}`);
return caches.delete(key);
}
})
);
})
.then(() => self.clients.claim())
);
});
Dans l'extrait de code ci-dessus, nous utilisons le waitUntil méthode sur le service worker. Cette méthode attend la fin de l'action, puis nous vérifions si les actifs que nous essayons d'effacer sont les actifs de notre application actuelle avant de les supprimer.
Ensuite, nous avons besoin des fichiers stockés dans notre cache pour les utiliser.
self.addEventListener("fetch", function (evt) {
evt.respondWith(
fetch(evt.request).catch(() => {
return caches.open(BLOGGER_ASSETS).then((cache) => {
return cache.match(evt.request);
});
})
);
})
Lorsqu'une demande est faite sur la page, PWA vérifiera notre cache et lira s'il y a des données dans le cache plutôt que d'aller sur le réseau. Ensuite, en utilisant le respondWith , nous remplaçons la valeur par défaut du navigateur et faisons en sorte que notre événement renvoie une promesse. Lorsque le cache est complet, nous pouvons retourner le cache correspondant à l'evt.request. Lorsque le cache est prêt, nous pouvons retourner le cache qui correspond à l'evt.request.
Nous avons mis en place avec succès notre travailleur de service. Maintenant, rendons-le disponible dans notre application.
Enregistrer le Service Worker
Inscrivons maintenant notre service worker dans notre public/js/app.js fichier avec l'extrait de code ci-dessous :
...
if ("serviceWorker" in navigator) {
window.addEventListener("load", function () {
navigator.serviceWorker
.register("/blogger.serviceWorker.js")
.then((res) => console.log("service worker registered"))
.catch((err) => console.log("service worker not registered", err));
});
}
Ici, nous vérifions si le navigateur de notre application prend en charge les service workers (bien sûr, tous les navigateurs ne prennent pas en charge les service workers), puis enregistrons notre fichier de service worker.
Lancez maintenant l'application avec la commande ci-dessous :
npm start
Accédez à localhost :8000 dans votre navigateur pour accéder à l'application.
Google Lighthouse Check
Vérifions maintenant si nous configurons correctement notre PWA à l'aide d'une vérification Google Lighthouse. Faites un clic droit sur votre navigateur et sélectionnez "inspecter". Dans les onglets d'inspection, sélectionnez phare et cliquez sur générer un rapport. Si tout s'est bien passé avec votre application, vous devriez voir une sortie comme celle de la capture d'écran ci-dessous :
Nous avons créé avec succès notre première application. Vous pouvez également arrêter le serveur pour tester l'application en mode hors connexion.
Conclusion
Les applications Web progressives (PWA) utilisent des API modernes pour fournir des fonctionnalités, une fiabilité et une installabilité améliorées avec une base de code unique. Ils permettent à votre utilisateur final d'utiliser votre application, qu'il dispose ou non d'une connexion Internet. Vous devriez vous sentir libre de bifurquer le référentiel et d'ajouter des fonctionnalités supplémentaires au projet. Bonne chance !