Avec Vue 2.x, on utilisait souvent Vue.extend pour créer des composants modaux ou de notification. Dans Vue 3.0, cette API a été supprimée. Voici comment réaliser une alternative avec createApp et la fonction h.
À l'usage : Message.info('Info'); , Message.error('Erreur');
1. Ajouter un point de montage dans index.html
<html lang="fr">
<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">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>Mon App</title>
</head>
<body>
<div id="app"></div>
<div id="notifications"></div>
</body>
</html>
2. Créer le service Message (message.ts)
Plutôt qu’une classe, nous utilisons une factory qui retourne un objet avec les méthodes souhaitées.
import { createApp, h, reactive } from 'vue';
import NotificationComponent from './NotificationComponent.vue';
interface Notification {
type: 'info' | 'warning' | 'success' | 'error';
text: string;
}
const notifications = reactive<Notification[]>([]);
const DUREE_AFFICHAGE = 3000; // ms
function ajouterEtAfficher(type: Notification['type'], texte: string): void {
// Ajouter une notification
notifications.push({ type, text: texte });
// Supprimer après délai
setTimeout(() => {
if (notifications.length > 0) {
notifications.shift();
}
}, DUREE_AFFICHAGE);
// Monter le composant s’il n’est pas déjà monté
const cible = document.getElementById('notifications');
if (cible && !cible.hasChildNodes()) {
const app = createApp({
render() {
return h(NotificationComponent, { liste: notifications });
}
});
app.mount('#notifications');
}
}
export const Message = {
info(texte: string): void {
ajouterEtAfficher('info', texte);
},
warning(texte: string): void {
ajouterEtAfficher('warning', texte);
},
success(texte: string): void {
ajouterEtAfficher('success', texte);
},
error(texte: string): void {
ajouterEtAfficher('error', texte);
}
};
export default Message;
3. Composant d’affichgae (NotificationComponent.vue)
<template>
<div class="notif-container">
<div
v-for="(notif, index) in liste"
:key="index"
:class="['notif-item', classeCss(notif.type)]"
>
{{ notif.text }}
</div>
</div>
</template>
<script setup lang="ts">
import { PropType } from 'vue';
interface Notification {
type: 'info' | 'warning' | 'success' | 'error';
text: string;
}
defineProps({
liste: {
type: Array as PropType<Notification[]>,
default: () => []
}
});
function classeCss(type: string): string {
const map: Record<string, string> = {
info: 'notif-info',
warning: 'notif-warning',
success: 'notif-success',
error: 'notif-error'
};
return map[type] || '';
}
</script>
<style lang="scss" scoped>
.notif-container {
position: fixed;
top: 16px;
right: 16px;
z-index: 9999;
display: flex;
flex-direction: column;
gap: 8px;
max-width: 280px;
}
.notif-item {
padding: 10px 20px;
border-radius: 4px;
background: #f0f9ff;
color: #0369a1;
border: 1px solid #bae6fd;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
font-size: 14px;
}
.notif-info { background: #e0f2fe; border-color: #7dd3fc; color: #0369a1; }
.notif-warning { background: #fef9c3; border-color: #fde047; color: #a16207; }
.notif-success { background: #dcfce7; border-color: #86efac; color: #15803d; }
.notif-error { background: #fee2e2; border-color: #fca5a5; color: #b91c1c; }
</style>
4. Utilisation dans un composant
<template>
<button @click="envoyerInfo">Afficher info</button>
<button @click="envoyerErreur">Afficher erreur</button>
</template>
<script setup lang="ts">
import Message from '@/services/message';
function envoyerInfo() {
Message.info('Opération réussie !');
}
function envoyerErreur() {
Message.error('Une erreur est survenue.');
}
</script>