Skill : Alpine.js Expert
Mission
Maîtriser Alpine.js dans un contexte production Laravel + Vite, en suivant les bonnes pratiques d'organisation et de séparation des responsabilités.
Philosophie Alpine.js
Locality of Behavior : Le comportement reste proche de la structure HTML. Privilégier toujours l'approche déclarative (x-data , x-on ) plutôt qu'impérative (document.querySelector ).
Analogie : "Alpine est à JavaScript ce que Tailwind est au CSS."
Installation & Configuration
🚫 CDN (Uniquement pour tutoriels/prototypes)
<!-- NE PAS utiliser en production Laravel --> <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
✅ Installation Professionnelle (Laravel + Vite)
- Installation via NPM
npm install alpinejs
- Configuration Vite (resources/js/app.js )
import './bootstrap'; import Alpine from 'alpinejs';
// Exposer Alpine globalement pour usage dans Blade window.Alpine = Alpine;
// Démarrer Alpine Alpine.start();
- Template Blade (resources/views/layouts/app.blade.php )
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> @vite(['resources/css/app.css', 'resources/js/app.js']) </head> <body> @yield('content') </body> </html>
Organisation du Code Alpine avec Laravel
❌ Mauvaise Pratique : Tout dans Blade
<!-- NE PAS FAIRE : Logique complexe inline --> <div x-data="{ items: [], search: '', async load() { /* 50 lignes de code... */ } }" x-init="load()"> <!-- Template complexe --> </div>
✅ Bonne Pratique : Composants Séparés
Structure Recommandée
resources/ ├── js/ │ ├── app.js # Point d'entrée principal │ ├── alpine/ │ │ ├── components/ # Composants Alpine réutilisables │ │ │ ├── articleManager.js │ │ │ ├── dropdown.js │ │ │ └── modal.js │ │ └── stores/ # Stores globaux (si nécessaire) │ │ └── cart.js │ └── utils/ # Helpers (debounce, etc.) └── views/ └── articles/ └── index.blade.php # Template propre
Définir un Composant (resources/js/alpine/components/articleManager.js )
export default () => ({ articles: [], searchQuery: '', isLoading: false,
async init() {
await this.loadArticles();
},
async loadArticles() {
this.isLoading = true;
try {
const response = await fetch('/api/articles');
this.articles = await response.json();
} finally {
this.isLoading = false;
}
},
async searchArticles() {
const response = await fetch(`/api/articles?q=${this.searchQuery}`);
this.articles = await response.json();
}
});
Enregistrer le Composant (resources/js/app.js )
import Alpine from 'alpinejs'; import articleManager from './alpine/components/articleManager';
// Enregistrer les composants Alpine.data('articleManager', articleManager);
window.Alpine = Alpine; Alpine.start();
Utiliser dans Blade (resources/views/articles/index.blade.php )
@extends('layouts.app')
@section('content') <div x-data="articleManager" x-init="init()"> <!-- Template propre et lisible --> <input type="text" x-model="searchQuery" @input.debounce.500ms="searchArticles()">
<div x-show="isLoading">Chargement...</div>
<ul x-show="!isLoading">
<template x-for="article in articles" :key="article.id">
<li x-text="article.title"></li>
</template>
</ul>
</div> @endsection
Directives Essentielles
État et Initialisation
-
x-data="{ key: value }" : État local
-
x-init="code" : Exécution à l'initialisation
Interactivité
-
@click="handler" : Événement (raccourci de x-on: )
-
:attr="value" : Attribut dynamique (raccourci de x-bind: )
-
x-model="var" : Binding bidirectionnel
Affichage
-
x-show="condition" : Toggle display: none (CSS)
-
x-if="condition" : Ajoute/Retire du DOM (sur <template> )
-
x-text="value" : Injecte texte
-
x-html="value" : Injecte HTML
Boucles
- x-for="item in items" : Itération (sur <template> , nécessite :key )
Magic Properties
-
$refs : Accès aux x-ref
-
$watch('prop', callback) : Observer changements
-
$dispatch('event', data) : Émettre événement custom
-
$nextTick(callback) : Attendre fin du rendu
Intégration Laravel
Passer Données PHP → Alpine
<div x-data="{ articles: @json($articles), csrfToken: '{{ csrf_token() }}' }"> </div>
Requêtes AJAX avec CSRF
// Dans le composant Alpine async createArticle(data) { const response = await fetch('/api/articles', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': this.csrfToken }, body: JSON.stringify(data) }); return await response.json(); }
Routes Laravel dans Alpine
<div x-data="{ apiUrl: '{{ route('api.articles.index') }}' }" x-init="fetch(apiUrl).then(...)"> </div>
Bonnes Pratiques
✅ À Faire
-
Utiliser Vite en production Laravel (jamais CDN)
-
Séparer composants complexes dans resources/js/alpine/components/
-
Un fichier par composant réutilisable
-
Utiliser :key dans x-for pour performances
-
Passer CSRF token pour requêtes POST/PUT/DELETE
-
Débounce les recherches (@input.debounce.500ms )
-
Gérer états de chargement (UX)
❌ À Éviter
-
Ne pas mélanger jQuery et Alpine
-
Ne pas manipuler le DOM directement (laisser Alpine gérer)
-
Éviter logique complexe inline dans Blade
-
Ne jamais oublier <template> avec x-for et x-if
-
Ne pas utiliser CDN en production Laravel
Patterns Courants (Voir examples.md )
-
Dropdown Menu
-
Modale avec Formulaire
-
Recherche avec Debounce
-
Suppression avec Confirmation
-
Tabs / Accordion
-
Toast Notifications
Modificateurs Utiles (Voir cheatsheet.md )
Événements : .prevent , .stop , .outside , .window , .debounce , .throttle
Touches : .enter , .escape , .space , .ctrl , .cmd
Transitions : x-transition , x-transition.duration.500ms
Contexte d'Utilisation
Idéal pour :
-
Applications server-side (Laravel Blade, PHP)
-
Enrichissement progressif HTML
-
Interactivité légère (UI components)
NON idéal pour :
-
SPA complexes avec routing (→ Vue/React)
-
Gestion d'état globale massive (→ Vuex/Redux)
Ressources
-
Doc Officielle : alpinejs.dev
-
Cheatsheet : Voir cheatsheet.md du skill
-
Exemples : Voir examples.md du skill