Llamar al Frontend desde Rust

Por: Artiko
taurieventosemitcanalesrust-frontend

Llamar al Frontend desde Rust

Hay tres formas de comunicarse desde Rust hacia el frontend.

Sistema de eventos

Eventos globales

Llegan a todas las ventanas:

use tauri::{AppHandle, Emitter};

#[tauri::command]
fn start_download(app: AppHandle, url: String) {
    // Logica de descarga...
    app.emit("download-started", &url).unwrap();
    app.emit("download-progress", 50).unwrap();
    app.emit("download-complete", true).unwrap();
}

Escuchar en JavaScript:

import { listen } from '@tauri-apps/api/event';

const unlisten = await listen('download-progress', (event) => {
    console.log(`Progreso: ${event.payload}%`);
});

// Dejar de escuchar al desmontar el componente
unlisten();

Eventos a una ventana especifica

use tauri::{AppHandle, Emitter};

#[tauri::command]
fn notify_settings(app: AppHandle) {
    app.emit_to("settings", "config-updated", "tema oscuro").unwrap();
}

Eventos con filtro

use tauri::{AppHandle, Emitter, EventTarget};

#[tauri::command]
fn notify_admins(app: AppHandle) {
    app.emit_filter("admin-alert", "Alerta!", |target| {
        matches!(target, EventTarget::WebviewWindow { label } if label.starts_with("admin"))
    }).unwrap();
}

Escuchar eventos del frontend en Rust

use tauri::Listener;

pub fn run() {
    tauri::Builder::default()
        .setup(|app| {
            app.listen("file-selected", |event| {
                println!("Archivo: {}", event.payload());
            });
            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error");
}

Emitir desde JavaScript

import { emit, emitTo } from '@tauri-apps/api/event';

// Global
await emit('file-selected', '/ruta/archivo.txt');

// A una ventana especifica
await emitTo('main', 'update-requested', { key: 'theme' });

Canales

Para transmisiones de alto volumen donde el orden importa. Mas rapidos que eventos para datos frecuentes:

use tauri::ipc::Channel;
use serde::Serialize;

#[derive(Serialize)]
struct LogEntry {
    level: String,
    message: String,
}

#[tauri::command]
async fn watch_logs(on_log: Channel<LogEntry>) {
    loop {
        let entry = LogEntry {
            level: "info".into(),
            message: "Proceso activo".into(),
        };
        if on_log.send(entry).is_err() {
            break; // Frontend desconectado
        }
        tokio::time::sleep(std::time::Duration::from_secs(1)).await;
    }
}

Evaluar JavaScript

Ejecuta codigo JS directamente en el webview:

use tauri::webview::WebviewWindow;

#[tauri::command]
async fn show_alert(window: WebviewWindow) {
    window.eval("alert('Hola desde Rust!')").unwrap();
}

Usa eval con cuidado — prefiere eventos o canales para comunicacion estructurada. eval es util para casos especificos como manipular el DOM directamente.

Cuando usar cada mecanismo

MecanismoUso ideal
Eventos globalesNotificar cambios de estado a todas las ventanas
Eventos dirigidosNotificar a una ventana especifica
CanalesStreaming de datos frecuentes (logs, progreso)
evalManipulacion directa del DOM (casos excepcionales)

Importante: limpieza

Siempre llama unlisten() al desmontar componentes para evitar memory leaks:

// React
useEffect(() => {
    const unlistenPromise = listen('my-event', handler);
    return () => { unlistenPromise.then(fn => fn()); };
}, []);

Resumen


← Llamar Rust desde el Frontend | Indice | Siguiente: Gestion de Estado →