Aller au contenu
Tauri

Appeler le frontend depuis Rust

Ce document comprend des guides sur la façon de communiquer avec le frontend de votre application depuis votre code Rust. Pour voir comment communiquer avec votre code Rust depuis votre frontend, consultez Appeler Rust depuis le Frontend.

Le côté Rust de votre application Tauri peut appeler le frontend en exploitant le système d’événements Tauri, en utilisant des canaux ou en évaluant directement du code JavaScript.

Tauri fournit un système d’événements simple que vous pouvez utiliser pour avoir une communication bidirectionnelle entre Rust et votre frontend.

Le système d’événements a été conçu pour les situations où de petites quantités de données doivent être diffusées ou vous devez implémenter un modèle multi-consommateur multi-producteur (par exemple, système de notification push).

Le système d’événements n’est pas conçu pour les situations à faible latence ou à haut débit. Voir la section des canaux pour l’implémentation optimisée pour la diffusion de données.

Les principales différences entre une commande Tauri et un événement Tauri sont que les événements n’ont pas de support de type fort, les charges utiles d’événements sont toujours des chaînes JSON, ce qui les rend inadaptées aux messages plus volumineux et il n’y a pas de support du système de capacités pour contrôler finement les données d’événements et les canaux.

Les types AppHandle et WebviewWindow implémentent les traits du système d’événements Listener et Emitter.

Les événements sont soit globaux (livrés à tous les auditeurs) soit spécifiques à la webview (uniquement livrés à la webview correspondant à une étiquette donnée).

Pour déclencher un événement global, vous pouvez utiliser la fonction Emitter#emit :

src-tauri/src/lib.rs
use tauri::{AppHandle, Emitter};
#[tauri::command]
fn download(app: AppHandle, url: String) {
app.emit("download-started", &url).unwrap();
for progress in [1, 15, 50, 80, 100] {
app.emit("download-progress", progress).unwrap();
}
app.emit("download-finished", &url).unwrap();
}

Pour déclencher un événement à un auditeur enregistré par une webview spécifique, vous pouvez utiliser la fonction Emitter#emit_to :

src-tauri/src/lib.rs
use tauri::{AppHandle, Emitter};
#[tauri::command]
fn login(app: AppHandle, user: String, password: String) {
let authenticated = user == "tauri-apps" && password == "tauri";
let result = if authenticated { "loggedIn" } else { "invalidCredentials" };
app.emit_to("login", "login-result", result).unwrap();
}

Il est également possible de déclencher un événement à une liste de webviews en appelant Emitter#emit_filter. Dans l’exemple suivant, nous émettons un événement d’ouverture de fichier aux webviews principale et visionneuse de fichiers :

src-tauri/src/lib.rs
use tauri::{AppHandle, Emitter, EventTarget};
#[tauri::command]
fn open_file(app: AppHandle, path: std::path::PathBuf) {
app.emit_filter("open-file", path, |target| match target {
EventTarget::WebviewWindow { label } => label == "main" || label == "file-viewer",
_ => false,
}).unwrap();
}

La charge utile de l’événement peut être n’importe quel type sérialisable qui implémente également Clone. Améliorons l’exemple d’événement de téléchargement en utilisant un objet pour émettre plus d’informations dans chaque événement :

src-tauri/src/lib.rs
use tauri::{AppHandle, Emitter};
use serde::Serialize;
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
struct DownloadStarted<'a> {
url: &'a str,
download_id: usize,
content_length: usize,
}
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
struct DownloadProgress {
download_id: usize,
chunk_length: usize,
}
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
struct DownloadFinished {
download_id: usize,
}
#[tauri::command]
fn download(app: AppHandle, url: String) {
let content_length = 1000;
let download_id = 1;
app.emit("download-started", DownloadStarted {
url: &url,
download_id,
content_length
}).unwrap();
for chunk_length in [15, 150, 35, 500, 300] {
app.emit("download-progress", DownloadProgress {
download_id,
chunk_length,
}).unwrap();
}
app.emit("download-finished", DownloadFinished { download_id }).unwrap();
}

Tauri fournit des API pour écouter les événements à la fois sur la webview et les interfaces Rust.

Le paquet NPM @tauri-apps/api offre des API pour écouter les événements globaux et spécifiques à la webview.

  • Écoute des événements globaux

    import { listen } from '@tauri-apps/api/event';
    type DownloadStarted = {
    url: string;
    downloadId: number;
    contentLength: number;
    };
    listen<DownloadStarted>('download-started', (event) => {
    console.log(
    `téléchargement de ${event.payload.contentLength} octets depuis ${event.payload.url}`
    );
    });
  • Écoute des événements spécifiques à la webview

    import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
    const appWebview = getCurrentWebviewWindow();
    appWebview.listen<string>('logged-in', (event) => {
    localStorage.setItem('session-token', event.payload);
    });

La fonction listen maintient l’écouteur d’événements enregistré pendant toute la durée de vie de l’application. Pour arrêter d’écouter un événement, vous pouvez utiliser la fonction unlisten qui est retournée par la fonction listen :

import { listen } from '@tauri-apps/api/event';
const unlisten = await listen('download-started', (event) => {});
unlisten();

De plus, Tauri fournit une fonction utilitaire pour écouter un événement exactement une fois :

import { once } from '@tauri-apps/api/event';
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
once('ready', (event) => {});
const appWebview = getCurrentWebviewWindow();
appWebview.once('ready', () => {});

Les événements globaux et spécifiques à la webview sont également livrés aux auditeurs enregistrés dans Rust.

  • Écoute des événements globaux

    src-tauri/src/lib.rs
    use tauri::Listener;
    #[cfg_attr(mobile, tauri::mobile_entry_point)]
    pub fn run() {
    tauri::Builder::default()
    .setup(|app| {
    app.listen("download-started", |event| {
    if let Ok(payload) = serde_json::from_str::<DownloadStarted>(&event.payload()) {
    println!("téléchargement {}", payload.url);
    }
    });
    Ok(())
    })
    .run(tauri::generate_context!())
    .expect("error while running tauri application");
    }
  • Écoute des événements spécifiques à la webview

    src-tauri/src/lib.rs
    use tauri::{Listener, Manager};
    #[cfg_attr(mobile, tauri::mobile_entry_point)]
    pub fn run() {
    tauri::Builder::default()
    .setup(|app| {
    let webview = app.get_webview_window("main").unwrap();
    webview.listen("logged-in", |event| {
    let session_token = event.data;
    // sauvegarder le jeton..
    });
    Ok(())
    })
    .run(tauri::generate_context!())
    .expect("error while running tauri application");
    }

La fonction listen maintient l’écouteur d’événements enregistré pendant toute la durée de vie de l’application. Pour arrêter d’écouter un événement, vous pouvez utiliser la fonction unlisten :

// unlisten en dehors de la portée du gestionnaire d'événements :
let event_id = app.listen("download-started", |event| {});
app.unlisten(event_id);
// unlisten lorsque certains critères d'événement sont remplis
let handle = app.handle().clone();
app.listen("status-changed", |event| {
if event.data == "ready" {
handle.unlisten(event.id);
}
});

De plus, Tauri fournit une fonction utilitaire pour écouter un événement exactement une fois :

app.once("ready", |event| {
println!("app est prêt");
});

Dans ce cas, l’écouteur d’événements est immédiatement désinscrit après son premier déclenchement.

Le système d’événements est conçu pour être une communication bidirectionnelle simple qui est disponible globalement dans votre application. Sous le capot, il évalue directement le code JavaScript, il peut donc ne pas convenir à l’envoi d’une grande quantité de données.

Les canaux sont conçus pour être rapides et fournir des données ordonnées. Ils sont utilisés en interne pour les opérations de streaming telles que la progression du téléchargement, la sortie du processus enfant et les messages WebSocket.

Réécrivons notre exemple de commande de téléchargement pour utiliser des canaux au lieu du système d’événements :

src-tauri/src/lib.rs
use tauri::{AppHandle, ipc::Channel};
use serde::Serialize;
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase", rename_all_fields = "camelCase", tag = "event", content = "data")]
enum DownloadEvent<'a> {
Started {
url: &'a str,
download_id: usize,
content_length: usize,
},
Progress {
download_id: usize,
chunk_length: usize,
},
Finished {
download_id: usize,
},
}
#[tauri::command]
fn download(app: AppHandle, url: String, on_event: Channel<DownloadEvent>) {
let content_length = 1000;
let download_id = 1;
on_event.send(DownloadEvent::Started {
url: &url,
download_id,
content_length,
}).unwrap();
for chunk_length in [15, 150, 35, 500, 300] {
on_event.send(DownloadEvent::Progress {
download_id,
chunk_length,
}).unwrap();
}
on_event.send(DownloadEvent::Finished { download_id }).unwrap();
}

Lors de l’appel de la commande de téléchargement, vous devez créer le canal et le fournir comme argument :

import { invoke, Channel } from '@tauri-apps/api/core';
type DownloadEvent =
| {
event: 'started';
data: {
url: string;
downloadId: number;
contentLength: number;
};
}
| {
event: 'progress';
data: {
downloadId: number;
chunkLength: number;
};
}
| {
event: 'finished';
data: {
downloadId: number;
};
};
const onEvent = new Channel<DownloadEvent>();
onEvent.onmessage = (message) => {
console.log(`got download event ${message.event}`);
};
await invoke('download', {
url: 'https://raw.githubusercontent.com/tauri-apps/tauri/dev/crates/tauri-schema-generator/schemas/config.schema.json',
onEvent,
});

Pour exécuter directement n’importe quel code JavaScript sur le contexte de la webview, vous pouvez utiliser la fonction WebviewWindow#eval :

src-tauri/src/lib.rs
use tauri::Manager;
tauri::Builder::default()
.setup(|app| {
let webview = app.get_webview_window("main").unwrap();
webview.eval("console.log('hello from Rust')")?;
Ok(())
})

Si le script à évaluer n’est pas si simple et doit utiliser des entrées d’objets Rust, nous recommandons d’utiliser le crate serialize-to-javascript.


© 2025 Tauri Contributors. CC-BY / MIT