Skip to content

useTauriEvent

Le composable useTauriEvent fournit une interface réactive pour écouter et émettre des événements Tauri, permettant une communication en temps réel entre votre backend Rust et votre frontend Vue.

Signature

typescript
function useTauriEvent<T = unknown>(eventName: string): TauriEventReturn<T>;

Paramètres

eventName

  • Type : string
  • Requis : Oui
  • Description : Le nom de l'événement Tauri à écouter

Valeur de Retour

Le composable retourne un objet avec les propriétés suivantes :

data

  • Type : Readonly<Ref<T | null>>
  • Description : Référence réactive contenant le dernier payload d'événement reçu

error

  • Type : Readonly<Ref<Error | null>>
  • Description : Référence réactive contenant toute erreur survenue

startListening

  • Type : () => Promise<void>
  • Description : Fonction pour commencer à écouter les événements

stopListening

  • Type : () => void
  • Description : Fonction pour arrêter d'écouter les événements

emit

  • Type : (payload: T) => Promise<void>
  • Description : Fonction pour émettre un événement avec le payload donné

Exemples d'Usage

Écoute d'Événements de Base

vue
<template>
    <div>
        <h3>Écouteur d'Événements</h3>

        <div>
            <button @click="startListening">Commencer l'écoute</button>
            <button @click="stopListening">Arrêter l'écoute</button>
        </div>

        <div v-if="error" class="error">Erreur : {{ error.message }}</div>

        <div v-if="data">
            <h4>Dernier Événement Reçu :</h4>
            <pre>{{ JSON.stringify(data, null, 2) }}</pre>
        </div>
    </div>
</template>

<script setup>
const { data, error, startListening, stopListening } =
    useTauriEvent("my-event");
</script>

Émission d'Événements

vue
<template>
    <div>
        <h3>Envoyer des Événements</h3>

        <input v-model="message" placeholder="Entrez un message" />
        <button @click="sendMessage" :disabled="!message">
            Envoyer l'événement
        </button>

        <div v-if="error" class="error">Erreur : {{ error.message }}</div>
    </div>
</template>

<script setup>
const message = ref("");

const { emit, error } = useTauriEvent("user-message");

const sendMessage = async () => {
    if (message.value.trim()) {
        await emit({
            message: message.value,
            timestamp: Date.now(),
        });
        message.value = "";
    }
};
</script>

Événements Typés avec TypeScript

vue
<template>
    <div>
        <h3>Application de Chat</h3>

        <div class="chat-controls">
            <button @click="startListening">Se connecter</button>
            <button @click="stopListening">Se déconnecter</button>
        </div>

        <div class="chat-messages">
            <div v-for="msg in messages" :key="msg.id" class="message">
                <strong>{{ msg.user }} :</strong> {{ msg.text }}
                <small>{{
                    new Date(msg.timestamp).toLocaleTimeString()
                }}</small>
            </div>
        </div>

        <div class="chat-input">
            <input
                v-model="newMessage"
                @keyup.enter="sendMessage"
                placeholder="Tapez un message..."
            />
            <button @click="sendMessage">Envoyer</button>
        </div>
    </div>
</template>

<script setup>
interface ChatMessage {
  id: string
  user: string
  text: string
  timestamp: number
}

const messages = ref<ChatMessage[]>([])
const newMessage = ref('')

const {
  data: receivedMessage,
  error,
  startListening,
  stopListening,
  emit
} = useTauriEvent<ChatMessage>('chat-message')

// Observer les nouveaux messages
watch(receivedMessage, (message) => {
  if (message) {
    messages.value.push(message)
  }
})

const sendMessage = async () => {
  if (newMessage.value.trim()) {
    const message: ChatMessage = {
      id: Date.now().toString(),
      user: 'Vous',
      text: newMessage.value,
      timestamp: Date.now()
    }

    await emit(message)
    newMessage.value = ''
  }
}

// Auto-connexion au montage du composant
onMounted(() => {
  startListening()
})
</script>

Surveillance du Système de Fichiers

vue
<template>
    <div>
        <h3>Surveillance du Système de Fichiers</h3>

        <div>
            <button @click="startWatching">Commencer la surveillance</button>
            <button @click="stopWatching">Arrêter la surveillance</button>
        </div>

        <div v-if="error" class="error">Erreur : {{ error.message }}</div>

        <div v-if="fileEvent">
            <h4>Événement du Système de Fichiers :</h4>
            <p><strong>Action :</strong> {{ fileEvent.action }}</p>
            <p><strong>Fichier :</strong> {{ fileEvent.path }}</p>
            <p>
                <strong>Heure :</strong>
                {{ new Date(fileEvent.timestamp).toLocaleString() }}
            </p>
        </div>

        <div class="recent-events">
            <h4>Événements Récents :</h4>
            <ul>
                <li v-for="event in recentEvents" :key="event.timestamp">
                    {{ event.action }} - {{ event.path }}
                </li>
            </ul>
        </div>
    </div>
</template>

<script setup>
interface FileSystemEvent {
  action: 'created' | 'modified' | 'deleted'
  path: string
  timestamp: number
}

const recentEvents = ref<FileSystemEvent[]>([])

const {
  data: fileEvent,
  error,
  startListening: startWatching,
  stopListening: stopWatching
} = useTauriEvent<FileSystemEvent>('file-system-event')

// Suivre les événements récents
watch(fileEvent, (event) => {
  if (event) {
    recentEvents.value.unshift(event)
    // Garder seulement les 10 derniers événements
    if (recentEvents.value.length > 10) {
      recentEvents.value = recentEvents.value.slice(0, 10)
    }
  }
})
</script>

Suivi de Progression

vue
<template>
    <div>
        <h3>Progression du Téléchargement</h3>

        <button @click="startDownload" :disabled="isDownloading">
            Commencer le téléchargement
        </button>

        <div v-if="isDownloading" class="progress-container">
            <div class="progress-bar">
                <div
                    class="progress-fill"
                    :style="{ width: `${progress?.percentage || 0}%` }"
                ></div>
            </div>
            <p>{{ progress?.percentage || 0 }}% - {{ progress?.status }}</p>
            <p v-if="progress?.bytesDownloaded">
                {{ formatBytes(progress.bytesDownloaded) }} /
                {{ formatBytes(progress.totalBytes) }}
            </p>
        </div>

        <div v-if="error" class="error">
            Échec du téléchargement : {{ error.message }}
        </div>
    </div>
</template>

<script setup>
interface DownloadProgress {
  percentage: number
  status: string
  bytesDownloaded: number
  totalBytes: number
  finished: boolean
}

const isDownloading = ref(false)

const {
  data: progress,
  error,
  startListening,
  stopListening,
  emit
} = useTauriEvent<DownloadProgress>('download-progress')

const startDownload = async () => {
  isDownloading.value = true
  await startListening()

  // Émettre la commande de début de téléchargement
  await emit({ action: 'start', url: 'https://example.com/file.zip' })
}

// Observer la fin du téléchargement
watch(progress, (prog) => {
  if (prog?.finished) {
    isDownloading.value = false
    stopListening()
  }
})

const formatBytes = (bytes: number) => {
  return (bytes / 1024 / 1024).toFixed(2) + ' MB'
}
</script>

<style scoped>
.progress-container {
    margin: 1rem 0;
}

.progress-bar {
    width: 100%;
    height: 20px;
    background-color: #f0f0f0;
    border-radius: 10px;
    overflow: hidden;
}

.progress-fill {
    height: 100%;
    background-color: #4caf50;
    transition: width 0.3s ease;
}
</style>

Gestion du Cycle de Vie

Le composable gère automatiquement le nettoyage :

vue
<script setup>
const { startListening, stopListening } = useTauriEvent("my-event");

// Commencer l'écoute au montage du composant
onMounted(() => {
    startListening();
});

// Le nettoyage est automatique au démontage du composant
// Pas besoin d'appeler manuellement stopListening dans onUnmounted
</script>

Gestion d'Erreurs

Gérer différents types d'erreurs :

vue
<script setup>
const { error, startListening } = useTauriEvent("my-event");

watchEffect(() => {
    if (error.value) {
        if (error.value.message.includes("Tauri API not available")) {
            console.log(
                "Fonctionnement en mode navigateur - événements désactivés"
            );
        } else {
            console.error("Erreur d'événement:", error.value);
            // Peut-être afficher une notification utilisateur
        }
    }
});
</script>

Bonnes Pratiques

1. Utiliser TypeScript pour les Payloads d'Événements

Définir des interfaces pour vos données d'événement :

typescript
interface UserAction {
    type: "login" | "logout" | "register";
    userId: string;
    timestamp: number;
}

const { data, emit } = useTauriEvent<UserAction>("user-action");

2. Gérer les États de Connexion

Fournir un retour sur l'état d'écoute :

vue
<template>
    <div>
        <span class="status" :class="{ listening: isListening }">
            {{ isListening ? "Connecté" : "Déconnecté" }}
        </span>
    </div>
</template>

<script setup>
const isListening = ref(false);
const { startListening, stopListening } = useTauriEvent("status");

const connect = async () => {
    await startListening();
    isListening.value = true;
};

const disconnect = () => {
    stopListening();
    isListening.value = false;
};
</script>

3. Débouncer les Événements Rapides

Pour les événements haute fréquence, considérez le debouncing :

vue
<script setup>
import { debounce } from "lodash-es";

const { data } = useTauriEvent("mouse-position");

const debouncedHandler = debounce((position) => {
    // Gérer la mise à jour de position
    console.log("Position de la souris:", position);
}, 100);

watch(data, debouncedHandler);
</script>

4. Valider les Données d'Événement

Toujours valider les données d'événement entrantes :

vue
<script setup>
const { data } = useTauriEvent("user-data");

watch(data, (userData) => {
    if (userData && typeof userData === "object" && "id" in userData) {
        // Les données sont valides
        handleUserData(userData);
    } else {
        console.warn("Données utilisateur invalides reçues:", userData);
    }
});
</script>

Intégration Backend Rust

Émettre des Événements depuis Rust

rust
use tauri::{AppHandle, Manager};

#[tauri::command]
fn process_data(app: AppHandle) -> Result<(), String> {
    // Faire du traitement...

    // Émettre un événement vers le frontend
    app.emit_all("data-processed", ProcessedData {
        id: 123,
        result: "Succès".to_string(),
        timestamp: chrono::Utc::now().timestamp(),
    }).map_err(|e| e.to_string())?;

    Ok(())
}

Écouter les Événements en Rust

rust
use tauri::{Manager, Window};

fn setup_event_listeners(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
    let window = app.get_window("main").unwrap();

    window.listen("user-action", |event| {
        println!("Action utilisateur reçue: {:?}", event.payload());
        // Gérer l'événement
    });

    Ok(())
}

Articles Liés