Cours Vue.js · Module 14: Directives et plugins
Créer des plugins utiles
Dans cette leçon3
Dans NOVA LAB, chaque système de la station est un module indépendant. Voyons comment créer des plugins réellement utiles : les notifications toast et l'i18n (internationalisation).
Toast Plugin - les notifications
Un toast est une courte notification affichée temporairement. Le plugin fournira une méthode show() disponible dans toute l'application :
1const ToastPlugin = {
2 install(app, options = {}) {
3 const duration = options.duration || 3000
4 const position = options.position || 'top-right'
5
6 const toasts = ref([])
7 let toastId = 0
8
9 const toast = {
10 show(message, type = 'info') {
11 const id = toastId++
12 toasts.value.push({ id, message, type })
13 setTimeout(() => {
14 toasts.value = toasts.value.filter(t => t.id !== id)
15 }, duration)
16 },
17 success(msg) { this.show(msg, 'success') },
18 error(msg) { this.show(msg, 'error') },
19 warning(msg) { this.show(msg, 'warning') },
20 list: toasts
21 }
22
23 app.provide('toast', toast)
24 }
25}Utilisation dans un composant :
1<script setup>
2import { inject } from 'vue'
3
4const toast = inject('toast')
5
6function onSave() {
7 toast.success('Données enregistrées !')
8}
9
10function onError() {
11 toast.error('Erreur de connexion au satellite !')
12}
13</script>i18n Plugin - support multilingue
Un plugin pour traduire des textes dans différentes langues :
1const I18nPlugin = {
2 install(app, options) {
3 const locale = ref(options.defaultLocale || 'pl')
4 const messages = options.messages || {}
5
6 function t(key) {
7 const keys = key.split('.')
8 let result = messages[locale.value]
9 for (const k of keys) {
10 result = result?.[k]
11 }
12 return result || key
13 }
14
15 function setLocale(lang) {
16 if (messages[lang]) {
17 locale.value = lang
18 }
19 }
20
21 app.provide('i18n', {
22 t,
23 locale,
24 setLocale,
25 availableLocales: Object.keys(messages)
26 })
27
28 app.directive('t', (el, binding) => {
29 el.textContent = t(binding.value)
30 })
31 }
32}Utilisation :
1app.use(I18nPlugin, {
2 defaultLocale: 'fr',
3 messages: {
4 fr: {
5 nav: { home: 'Accueil', settings: 'Paramètres' },
6 status: { online: 'Connecté', offline: 'Déconnecté' }
7 },
8 en: {
9 nav: { home: 'Home', settings: 'Settings' },
10 status: { online: 'Connected', offline: 'Disconnected' }
11 }
12 }
13})Dans un composant :
1<script setup>
2import { inject } from 'vue'
3const { t, setLocale, locale } = inject('i18n')
4</script>
5
6<template>
7 <nav>
8 <a>{{ t('nav.home') }}</a>
9 <a>{{ t('nav.settings') }}</a>
10 </nav>
11 <p>Status: {{ t('status.online') }}</p>
12 <button @click="setLocale('en')">EN</button>
13 <button @click="setLocale('fr')">FR</button>
14</template>Bonnes pratiques pour les plugins
- Provide/Inject - à préférer aux globalProperties
- Options avec valeurs par défaut - le plugin doit fonctionner sans configuration
- Typage - exporte des interfaces pour TypeScript
- Nettoyage - si le plugin modifie le DOM, assure-toi de bien nettoyer
- Documentation - décris les options et méthodes disponibles
Code de cette leçon : App.vue
1<script setup>
2import { inject, ref } from 'vue'
3
4const toast = inject('toast')
5const { t, locale, setLocale } = inject('i18n')
6
7function showSuccess() {
8 toast.success(t('toast.saved'))
9}
10function showError() {
11 toast.error(t('toast.connectionError'))
12}
13function showWarning() {
14 toast.warning(t('toast.lowOxygen'))
15}
16</script>
17
18<template>
19 <div class="nova-lab">
20 <h1>{{ t('title') }}</h1>
21
22 <section class="section">
23 <h2>Toast Plugin</h2>
24 <div class="btn-row">
25 <button @click="showSuccess" class="btn success">{{ t('toast.successBtn') }}</button>
26 <button @click="showError" class="btn error">{{ t('toast.errorBtn') }}</button>
27 <button @click="showWarning" class="btn warn">{{ t('toast.warnBtn') }}</button>
28 </div>
29 </section>
30
31 <section class="section">
32 <h2>i18n Plugin</h2>
33 <p>{{ t('status.label') }}: {{ t('status.online') }}</p>
34 <p>{{ t('crew.label') }}: {{ t('crew.count') }}</p>
35 <div class="btn-row" style="margin-top: 10px">
36 <button
37 @click="setLocale('fr')"
38 :class="['btn', { active: locale === 'fr' }]"
39 >FR</button>
40 <button
41 @click="setLocale('en')"
42 :class="['btn', { active: locale === 'en' }]"
43 >EN</button>
44 </div>
45 <p class="hint">Change de langue et observe comment les textes changent</p>
46 </section>
47
48 <!-- Conteneur des toasts -->
49 <div class="toast-container">
50 <div
51 v-for="t in toast.list.value"
52 :key="t.id"
53 :class="['toast', t.type]"
54 >
55 {{ t.message }}
56 </div>
57 </div>
58 </div>
59</template>
60
61<style scoped>
62.nova-lab {
63 background: #0a0e27;
64 color: #c0c0c0;
65 min-height: 100vh;
66 padding: 20px;
67 font-family: monospace;
68}
69h1 { color: #00ff88; margin-bottom: 20px; }
70h2 { color: #00b4d8; font-size: 16px; margin-bottom: 10px; }
71.section {
72 background: rgba(0,180,216,0.05);
73 border: 1px solid rgba(0,180,216,0.2);
74 padding: 15px;
75 border-radius: 8px;
76 margin-bottom: 15px;
77}
78.btn-row { display: flex; gap: 10px; }
79.btn {
80 background: rgba(0,255,136,0.15);
81 color: #00ff88;
82 border: 1px solid #00ff88;
83 padding: 8px 16px;
84 border-radius: 6px;
85 cursor: pointer;
86 font-family: monospace;
87}
88.btn.active { background: #00ff88; color: #0a0e27; }
89.btn.success { color: #2ecc71; border-color: #2ecc71; }
90.btn.error { color: #e74c3c; border-color: #e74c3c; }
91.btn.warn { color: #f39c12; border-color: #f39c12; }
92.toast-container {
93 position: fixed; top: 20px; right: 20px;
94 display: flex; flex-direction: column; gap: 8px;
95 z-index: 1000;
96}
97.toast {
98 padding: 12px 20px;
99 border-radius: 8px;
100 font-family: monospace;
101 font-size: 14px;
102 animation: slideIn 0.3s ease;
103}
104.toast.success { background: rgba(46,204,113,0.9); color: white; }
105.toast.error { background: rgba(231,76,60,0.9); color: white; }
106.toast.warning { background: rgba(243,156,18,0.9); color: white; }
107.toast.info { background: rgba(0,180,216,0.9); color: white; }
108@keyframes slideIn {
109 from { transform: translateX(100%); opacity: 0; }
110 to { transform: translateX(0); opacity: 1; }
111}
112.hint { color: #666; font-size: 12px; margin-top: 8px; }
113</style>Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Comment implémenter des toasts qui disparaissent automatiquement après 3 secondes ?
2. Que fait la fonction t('nav.home') dans un plugin i18n ?
Tâches pratiques dans le jeu
- Tri vertical
Ordonne les étapes du fonctionnement de la méthode toast.show() dans le bon ordre :
- Éditeur de code
Implémente un ToastPlugin avec un tableau réactif de toasts et les méthodes show/success/error.
- Clique dans l'ordre
Ordonne la syntaxe correcte de app.provide dans un plugin :
- Tri horizontal
Ordonne l'appel inject correct pour récupérer le toast :