Comment j'ai construit ce blog et pourquoi ?

2026/04/20
Webdev Débutant Node.js Apprentissage

Introduction

J'ai voulu faire ce projet de "portfolio + blog", car j'avais l'ambition de revenir à des éléments fondamentaux du développement web. Des connaissances qui seraient toujours bonnes à avoir dans sa boîte à outils de développeur. Depuis une dizaine d'années, les formations en ligne ou dans certaines écoles privées boostées à l'argent public, ont des enseignements très... En surface.

Pourquoi s'embêter d'enseigner comment fonctionne un navigateur ou manipuler le DOM quand les frameworks s'occupent de tout pour vous, avec en plus un fonctionnement plus complexe à cause de l'expansion des SPA (Single Page Application) pour un pauvre site statique ?

L'envie d'apprendre en échouant

J'avais envie de revoir les bases. C'est-à-dire :

Faire un site statique, quoi.

De plus, je me suis confronté à pas mal de contre-vérités que j'avais en tête :

Bref, je pensais que ce projet "tranquille" serait simple, mais entre les petites expérimentations pas abouties et la mise en production d'un produit, c'est une autre histoire.

La partie blog : SSG et CMS

En plus d'un site personnel minimaliste, je voulais aussi avoir un côté blog pour mettre à plat mon apprentissage au quotidien, pouvoir prendre du recul sur ce que je fais, ainsi que les outils que j'utilise pour des projets.

CMS (Content Manager System)

SSG (Static Site Generation)

On arrive maintenant aux générateur de sites statiques, comme : Jekyll, Hugo ou encore Astro, etc.

Si je devais refaire ce projet, je serais certainement parti entre ces trois choix. Ils ont chacun leurs avantages et inconvénients. Astro aurait été le plus familier, je pense.

Gestion du Markdown

Plutôt que d'adopter un SSG tout fait, j'ai voulu gérer par moi-même le parsing des articles de blog. En effet, chaque article est un fichier .md stocké dans src/views/content/.

J'utilise deux librairies :

Ci-dessous le service qui lit et parse les articles :

export async function getPosts(files: string[]) {
  const posts = await Promise.all(
    files.map(async (file) => {
      const content = await fs.readFile(path.join(CONTENT_DIR, file), "utf-8");
      const { data } = matter(content);
      return {
        slug: file.replace(".md", ""),
        title: data.title,
        description: data.description,
        date: new Date(data.date),
        image: data.image,
        tags: Array.isArray(data.tags)
          ? data.tags
          : data.tags
            ? [data.tags]
            : []
      };
    })
  );
  return posts;
}

matter(content) sépare le frontmatter du corps de l'article. Le reste, comme la conversion Markdown vers HTML, est ensuite délégué au moteur de template au moment du rendu.

C'est une approche minimaliste, sans base de données ni CMS : les articles sont de simples fichiers texte versionnés avec le reste du code.

La stack technique

Architecture du projet

Avant de rentrer dans le détail, je vais présenter la structure générale du projet, qui est une structure en couches afin de séparer au mieux les responsabilités :

Node.js avec Express

Au final, je suis parti sur une stack technique minimaliste et là où je suis le plus à l'aise. Je suis donc parti sur le runtime Javascript Node.js en utilisant le framework Express, qui permet de construire des serveurs web simplement, et avec le juste nécessaire.

Moteur de template avec EJS

J'ai besoin d'un moteur de template : j'ai choisi ce bon vieux EJS. Il est simple à prendre en main, encore maintenu et facilement intégrable avec Express.

Edgejs, le moteur builtin d'Adonis m'a fait de l'oeil, mais la mise en place était un peu plus laborieux.

EJS me permet tout de même de faire des "composants" avec les partials, j'ai pu découpé certains block comme les cards que j'utilise pour les sections projets et articles.

<article class="card">
  <img
    class="card-image"
    src="/images/<%= post.image %>"
    alt="card image"
    loading="lazy"
    decoding="async"
  />
  <div class="card-content">
    <a href="/blog/<%= post.slug %>">
      <h3 class="card-title"><%= post.title %></h3>
    </a>
    <p class="card-description"><%= post.description %></p>
    <small> <%= post.date %> </small>
    <div class="container">
      <% for (tag of post.tags) { %>
      <a href="/blog/tag/<%= tag %>">
        <small class="badge"> <%= tag %> </small>
      </a>
      <% } %>
    </div>
  </div>
</article>

Puis on va l'injecter dans notre page :

<section class="section">
  <h2>Dernières Publications</h2>
  <article class="section items-center">
    <% posts.forEach(post => { %> <%- include('../card-article', { post: post })
    %> <% }) %>
  </article>
  <!--[...reste du code]-->
</section>

Style : CSS Natif

Côté style, j'ai choisi de faire du CSS natif et de ne pas utiliser les nombreux CSS-in-JS framework. J'ai commencé le développement en 2023, j'aimerais me mettre à jour sur les nouveautés CSS.

Je trouve que nativement, on peut remplacer pas mal de Javascript qui alourdissent bêtement des applications ou des sites. Autant profiter des évolutions positives, tout en gardant un oeil sur la compatibilité des navigateurs avec celles-ci.

Je voulais aussi aborder rapidement le fait de partir sur un design "mobile-first" pour me faciliter la vie sur le responsive des pages, ainsi que le intrinsic patterns qui permet de se baser sur la taille d'un élément sur son contenu, sans se soucier de son contexte. Ce qui m'a permis d'avoir moins de media queries éparpiller dans ma feuille de style.

Performance et Accessibilité

Cela va de paire, certaines personnes pensent encore qu'un société à croissance infini dans un monde avec des ressources limités, ca leur semble logique. Bref.

Pourquoi mon site ou mon aplication, où le but principal est de transmettre des informations ne pourrait pas fonctionner sans CSS ou du Javascript ?

Voir même, ne vous sentez pas obligé d'acheter le dernier smartphone pour voir qu'actuellement je suis en alternance. Vous vous rappellez quand c'était trendy de parler de GreenIT ?

a11y

Bref, je veux que mon site puisse accueillir le maximum de personne, que ce soit avec un Iphone 3Gs ou des personnes ayant des troubles.

Ca sera une amélioration continu des normes RGAA et WAI. Ce n'est pas parfait, j'ai appris beaucoup de chose à ce niveau-là. Il faudrait que je me forme plus profondément sur ce sujet à l'avenir.

Performance

Au niveau des performances, même pour un site simple, la performance repose surtout sur des optimisations "basiques" qui ne coutent rien terme cognitive : images, compression et cache.

Les Images

Les images sont rapidement devenues le principal facteur de ralentissement avant que tout soit en cache, ce qui n'est pas du optimal. J'ai appliqué quelques bonnes pratiques pour la gestion de la performance des images, comme peut le faire Next.js avec son composant <Image /> :

Exemple :

<img
  class="card-image"
  src="/images/<%= post.image %>"
  alt="card image"
  loading="lazy"
  decoding="async"
/>

On peut voir que même sans Javascript, des images mal optimisées peuvent affecter lourdement les performances.

La Compression HTTP

Même si je n'envoie "que" du HTML et du CSS, la compression HTTP est indispensable.

J'utilise le middleware compression, qui active automatiquement gzip ou brotli selon le navigateur.

exemple de résultat loading=

Ce qui fait environ 75% de réduction, et donc moins de bande passante consommée et un temps de chargement plus rapide.

C'est probablement l'optimisation avec le meilleur ratio effort et gain.

Le Cache

Le cache navigateur est extrêmement puissant mais on peut vite se prendre les pieds dans le tapis.

J'ai configuré un cache assez long pour mes assets statiques (CSS et images) :

app.use(
  express.static("public", {
    maxAge: "30d", // cache de 30 jours
    immutable: true // Pour éviter tout revalidation inutile
  })
);

Cela permet au navigateur de ne jamais re-télécharger ces fichiers tant qu'ils ne changent pas.

Le problème que j'ai rencontré et qui assez classique c'est :

Comment recharger le navigateur pour récupérer la nouvelle version lorsqu'un fichier change ?

J'ai découvert le cache busting, qui est simplement la destruction de ce qui stocké en cache ou du moins seulement les fichiers qui ont changés.

J'ai résolu ce problème en modifiant le nom du fichier avec un hash unique :

import crypto from "crypto";

const hash = crypto
  .createHash("sha1")
  .update(minified)
  .digest("hex")
  .slice(0, 8);

Ce qui donne à la fin : bundle.${hash}.css

Ca me permet d'avoir un cache agressif et d'éviter d'avoir du contenu obsolète en production.

La Minification CSS

Même avec du CSS "simple", le poids peut vite augmenter à mesure que le site évolue.

J'ai d'abord utilisé clean-css pour minifier mes fichiers, ce qui permet de :

Cependant, le projet est en maintenance. Donc, pour des projets plus complexes, je me tournerai vers des alternatives comme cssnano ou esbuild, qui sont plus pertinentes et activement maintenues.

Je reste tout de même avec clean-css car ca reste une minification simple, c'est suffisant.

Conclusion

Ça m'a fait du bien de revenir à des bases et comprendre pourquoi le web moderne est dans l'état actuel. Je vois maintenant comment certains outils fonctionnent ainsi que leur utilité - Merci les outils comme Vite ou Webpack d'exister.

Ce projet m'a aussi donné envie d'explorer des sujets comme l'accessibilité, les Web Components, la sécurité ou encore la génération d'un flux RSS que je n'ai pas trop détaillé ici, mais qui fera peut être le sujet d'un prochain article ,plus court. On verra si j'arrive à me tenir à un article tous les deux mois.