Comment nous avons refondu le blog Laravel Blade hérité, débloqué les articles masqués, intégré la recherche en temps réel, la pagination et un moteur de recommandation intelligent.
Les blogs et moteurs de contenu sont au cœur de l'acquisition d'utilisateurs en SaaS. Néanmoins, les blogs marketing créés dans les premières phases d'un projet accumulent souvent de la dette technique.
Dans notre cas, le blog hérité était au point mort : il ne comportait qu'une page d'accueil basique et une vue d'article unitaire. Des éléments de navigation essentiels — tels que le bouton "Charger plus" — étaient littéralement commentés dans le code d'origine, bloquant l'accès aux articles plus anciens !
Voici comment nous avons refondu et migré le blog Laravel Blade vers une application Vue 3 réactive incluant un catalogue complet, la recherche instantanée, la pagination dynamique, des filtres par tags et un moteur de recommandation d'articles.
Pour corriger la navigation bloquée, nous avons créé une route dédiée /articles et développé un gestionnaire de pagination asynchrone remplaçant le code hérité commenté.
class=class="text-emerald-500 dark:text-emerald-400 font-medium">"text-slate-500 italic">// router/index.js
{
path: '/blog',
component: BlogHomeView,
},
{
path: '/blog/articles',
component: ArticleListingView,
},
{
path: '/blog/articles/:slug',
component: ArticleDetailView,
}Nous avons développé un composant de liste réactif qui filtre les articles dynamiquement par titre, tag ou catégorie avec des requêtes temporisées (debounced) pour optimiser les performances serveur.
<!-- components/ArticleListingView.vue -->
<template>
<div class="max-w-6xl mx-auto py-12 px-4">
<!-- Barre de recherche & Puces de tags -->
<div class="flex flex-col md:flex-row justify-between gap-4 mb-8">
<input
v-model="searchQuery"
@input="debouncedSearch"
placeholder="Rechercher des articles par mot-clé ou tag..."
class="w-full md:w-96 border rounded-xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<div class="flex flex-wrap gap-2">
<button
v-for="tag in availableTags"
:key="tag"
@click="toggleTag(tag)"
:class="selectedTag === tag ? 'bg-blue-600 text-white' : 'bg-slate-100 text-slate-700'"
class="px-3 py-1.5 rounded-lg text-xs font-semibold uppercase"
>
#{{ tag }}
</button>
</div>
</div>
<!-- Grille de Cartes d'Articles -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<article v-for="article in paginatedArticles" :key="article.id" class="border rounded-2xl p-6 shadow-sm">
<span class="text-xs font-bold uppercase text-blue-600 mb-2 block">{{ article.category }}</span>
<h3 class="text-lg font-bold mb-2">{{ article.title }}</h3>
<p class="text-xs text-slate-500 line-clamp-3 mb-4">{{ article.excerpt }}</p>
<router-link :to="class="text-emerald-500 dark:text-emerald-400 font-medium">`/blog/articles/${article.slug}`" class="text-xs font-bold text-blue-600 hover:underline">
Lire l'article →
</router-link>
</article>
</div>
</div>
</template>Pour stimuler l'engagement des lecteurs, nous avons implémenté un algorithme sur la page de détail calculant la pertinence thématique d'après le recoupement des tags et des catégories :