Accueil » Culture web » C’est quoi les SPA ?

C’est quoi les SPA ?

SPA pour Single Page Application est une implémentation d’une application web, elle consiste à mettre à jour le contenu de la page lors de la navigation au lieu de recharger la page entièrement. Les SPA sont très répandues de nos jours grâce au nombreux frameworks et librairies qui facilitent leurs mises en œuvre. Les SPA sont rendu possible par le JavaScript, longtemps utilisé pour créer des animations, à partir des années 2000, l’implémentation de l’AJAX a totalement redéfini son utilisation. L’AJAX consistant à faire un appel asynchrone vers un serveur qui va nous renvoyer des données que l’on va pouvoir utiliser pour modifier le contenu de notre page web sans recharger la page.

Comment fonctionnent les applications SPA ?

Les SPA se basent sur l’architecture Client-Serveur, l’application coté client va demander des données au serveur et ensuite elle va s’occuper de gérer l’affichage, il s’agit de client-side rendering. Le client-side rendering consiste à laisser le JavaScript de l’application coté client générer le HTML.

Explication client-side rendering et serveur-side rendering

Les SPA sont des applications qui implémente le client-side rendering, ce qui requière une très bonne gestion du State. Le state management est très important dans le web, qui plus est dans le client-side rendering puisqu’on peut aussi gérer le state dans l’application coté client. Pour facilité la création de SPA, on a des outils qui on fait leur preuve comme Angular, React ou encore VueJS, ces outils peuvent aussi s’occuper du state management avec des librairies comme NgRX pour Angular par exemple.

Créer une SPA en vanilla JavaScript ?

Afin d’un peu mieux comprendre comment ça fonctionne une SPA, nous alons en créer une simple. Evidemment, celle que je vais vous faire créer sera complexe pour pas grand chose, des simples pages HTML ferait le même résultat, mais c’est toujours bon de savoir comment ça peut s’articuler derrière tous ça.

Étape 1 : Créer notre structure HTML

Nous allons utiliser une base HTML très basique. On met notre JavaScript en type module pour pouvoir effectuer des imports.

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Une SPA</title>
        <script src="js/main.js" type="module" defer></script>
        <link rel="stylesheet" href="css/style.css" />
    </head>
    <body>
        <div id="app"></div>
    </body>
</html>

Étape 2 : Créer une class Page

On créer une class Page qui sera la class parents de toutes les class de nos pages. On lui donne quelque propriété ainsi qu’une method pour ajouter un lien vers cet page.

export default class Page {
    constructor(container, name) {
        this.container = container;
        this.name = name;
    }

    addNavigation(navigation) {
        const link = document.createElement("a");
        link.setAttribute("href", `#${this.name}`);
        link.text = this.name;
        navigation.appendChild(link);

        return this;
    }
}

Étape 3 : Implémenter des class pour les pages de notre SPA

Pour toutes les pages que l’on veut créer pour notre SPA, nous allons créer une class qui extends notre class Page. Cet class devra a minima extend le consctrutor de notre class parent et implémenter la method render. La method render permet de gérer l’affichage de notre page dans notre SPA. J’ai fait un mock d’une requête HTTP qui contient du JSON pour pouvoir faire utiliser l’API Fetch et ainsi faire semblant de récupérer des données d’un serveur.

import Page from "./page.js";

export default class Home extends Page {
    constructor(container) {
        super(container, "home");
    }

    async render() {
        const response = await fetch("./http/home.json");
        const content = await response.json();

        this.container.innerHTML = `<h1>${content.title}</h1><p>${content.content}</p>`;

        return this;
    }
}
import Page from "./page.js";

export default class Articles extends Page {
    constructor(container) {
        super(container, "articles");
    }

    async render() {
        const response = await fetch("./http/articles.json");
        const content = await response.json();
        
        this.container.innerHTML = `<h1>${content.title}</h1>`;
        content.articles.forEach((a) => {
            this.container.innerHTML += `<div><h2>${a.title}</h2><p>${a.excerpt}</p></div>`;
        });

        return this;
    }
}

Voici un exemple de JSON recupéré avec fetch.

{
    "title": "Mes articles",
    "articles": [
        {
            "title": "Mon article sur les SPA",
            "excerpt": "Les SPA c'est facile à faire"
        },
        {
            "title": "Pense à m'ajouter sur Linkedin",
            "excerpt": "Alexis GUAY sur Linkedin"
        }
    ]
}

Étape 4 : Créer une class App qui va géré notre application

Ensuite nous créons une class App qui s’occupera de créer tous se qu’on a besoin pour notre application. Dans son constructor on créé un élément pour gérer la navigation et un qui contiendra nos pages. Puis nous lui ajoutons nos pages et on les ajoutent dans notre navigation.

import Articles from "./articlesPage.js";
import Contact from "./contactPage.js";
import Home from "./homePage.js";

export default class App {
    constructor(element) {
        this.navigation = document.createElement("nav");
        element.appendChild(this.navigation);

        this.container = document.createElement("div");
        element.appendChild(this.container);

        this.pages = {
            home: new Home(this.container).addNavigation(this.navigation),
            articles: new Articles(this.container).addNavigation(this.navigation),
            contact: new Contact(this.container).addNavigation(this.navigation),
        };
    }
}

Étape 5 : Gérer le routing dans notre class App

On fait le choix de prendre le state de l’url comme source de vérité et donc chaque changement dans l’url occasionnera un changement dans notre application. Dans notre SPA, les potentiels changement dans l’url s’effectueront au niveau du hash de l’url (hash = ancre = anchor = #). On va commencer par regarder la première par à affiché à notre utilisateur et la render. Afin de gérer le changement de page, nous allons utiliser l’event ‘hashchange’ de window qui nous permettra de détecter les changement du hash de notre url. A chaque changement de hash dans notre url nous allons render la page qui correspond au nouveau hash.

const firstPageCandidate = location.hash.replace("#", "");
this.pages[firstPageCandidate].render();

window.addEventListener("hashchange", (e) => {
     const pageCandidate = location.hash.replace("#", "");
     this.pages[pageCandidate].render();
});

Étape 6 : Initialisé notre application

Maintenant qu’on a créé nos pages et notre application il nous reste plus qu’a la faire rentrer en action, pour ça on a juste à créer un instance de App.

import App from "./app.js";

((initSPA = true) => {
    if (false === initSPA) {
        return;
    }

    const appContainer = document.getElementById("app");
    new App(appContainer);
})();

En résumé, une SPA single page application, c’est quoi ?

  • Une SPA est une application web qui fonctionne sur une seule page. Au lieu de recharger toute la page à chaque action de l’utilisateur, elle met à jour dynamiquement les parties de la page nécessaires.
  • Les frameworks JavaScript modernes comme Angular, React et Vue.js, facilitent la création d’applications hautement interactives et responsives.
  • Les SPA (Single Page Application) permettent aux utilisateurs d’expérimenter une navigation fluide et rapide, car une fois que la page est chargée, les ressources sont conservées en mémoire, évitant ainsi les temps de chargement et les allers-retours vers le serveur.
  • De plus en plus populaires pour les applications web complexes et interactives, telles que les réseaux sociaux et les Saas.

Proposition de lectures

prototype inheritance
Comprendre le prototypal inheritance en JavaScript

Le prototypal inheritance nous permet d’étendre un objet pour qu’il accède aux propriété et fonction d’un autre.

le lazy-loading
Comprendre le lazy loading

Le lazy loading est un moyen de chargé en différé des images, du contenu et des modules.