Saltearse al contenido
Tauri

Llamar al frontend desde Rust

Este documento incluye guías sobre cómo comunicarse con el frontend de tu aplicación desde tu código Rust. Para ver cómo comunicarse con tu código Rust desde tu frontend, consulta Llamar a Rust desde el frontend.

El lado de Rust de tu aplicación Tauri puede llamar al frontend aprovechando el sistema de eventos de Tauri, usando canales o evaluando directamente el código JavaScript.

Tauri incluye un sistema de eventos simple que puedes usar para tener una comunicación bidireccional entre Rust y tu frontend.

El sistema de eventos se diseñó para situaciones en las que es necesario transmitir pequeñas cantidades de datos o necesitas implementar un patrón de múltiples consumidores y múltiples productores (por ejemplo, sistema de notificaciones push).

El sistema de eventos no está diseñado para situaciones de baja latencia o alto rendimiento. Consulta la sección de canales para la implementación optimizada para la transmisión de datos.

Las principales diferencias entre un comando de Tauri y un evento de Tauri son que los eventos no tienen soporte de tipo fuerte, las cargas útiles de eventos son siempre cadenas JSON, lo que las hace inadecuadas para mensajes más grandes y no hay soporte del sistema de capacidades para controlar de forma granular los datos de eventos y canales.

Los tipos AppHandle y WebviewWindow implementan los rasgos del sistema de eventos Listener y Emitter.

Los eventos son globales (se entregan a todos los oyentes) o específicos de webview (solo se entregan a la vista web que coincide con una etiqueta dada).

Para activar un evento global, puedes usar la función 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();
}

Para activar un evento a un oyente registrado por una vista web específica, puedes usar la función 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();
}

También es posible activar un evento a una lista de vistas web llamando a Emitter#emit_filter. En el siguiente ejemplo, emitimos un evento de abrir archivo a las vistas web principal y visor de archivos:

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 carga útil del evento puede ser cualquier tipo serializable que también implemente Clone. Mejoremos el ejemplo del evento de descarga usando un objeto para emitir más información en cada evento:

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 proporciona API para escuchar eventos tanto en la vista web como en las interfaces de Rust.

El paquete NPM @tauri-apps/api ofrece APIs para escuchar eventos tanto globales como específicos de la vista web.

  • Escuchar eventos globales

    import { listen } from '@tauri-apps/api/event';
    type DownloadStarted = {
    url: string;
    downloadId: number;
    contentLength: number;
    };
    listen<DownloadStarted>('download-started', (event) => {
    console.log(
    `descargando ${event.payload.contentLength} bytes desde ${event.payload.url}`
    );
    });
  • Escuchar eventos específicos de la vista web

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

La función listen mantiene el detector de eventos registrado durante toda la vida útil de la aplicación. Para dejar de escuchar un evento, puedes usar la función unlisten que devuelve la función listen:

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

Además, Tauri proporciona una función de utilidad para escuchar un evento exactamente una vez:

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

Los eventos globales y específicos de la vista web también se entregan a los oyentes registrados en Rust.

  • Escuchar eventos globales

    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!("downloading {}", payload.url);
    }
    });
    Ok(())
    })
    .run(tauri::generate_context!())
    .expect("error while running tauri application");
    }
  • Escuchar eventos específicos de la vista web

    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;
    // save token..
    });
    Ok(())
    })
    .run(tauri::generate_context!())
    .expect("error while running tauri application");
    }

La función listen mantiene el detector de eventos registrado durante toda la vida útil de la aplicación. Para dejar de escuchar un evento, puedes usar la función unlisten:

// unlisten outside of the event handler scope:
let event_id = app.listen("download-started", |event| {});
app.unlisten(event_id);
// unlisten when some event criteria is matched
let handle = app.handle().clone();
app.listen("status-changed", |event| {
if event.data == "ready" {
handle.unlisten(event.id);
}
});

Además, Tauri proporciona una función de utilidad para escuchar un evento exactamente una vez:

app.once("ready", |event| {
println!("app is ready");
});

En este caso, el detector de eventos se cancela inmediatamente después de su primer desencadenante.

El sistema de eventos está diseñado para ser una comunicación bidireccional simple que está disponible globalmente en tu aplicación. Debajo del capó, evalúa directamente el código JavaScript, por lo que podría no ser adecuado para enviar una gran cantidad de datos.

Los canales están diseñados para ser rápidos y entregar datos ordenados. Se utilizan internamente para operaciones de transmisión como el progreso de descarga, la salida del proceso secundario y los mensajes WebSocket.

Reescribamos nuestro ejemplo de comando de descarga para usar canales en lugar del sistema de eventos:

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();
}

Al llamar al comando de descarga, debes crear el canal y proporcionarlo como argumento:

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,
});

Para ejecutar directamente cualquier código JavaScript en el contexto de la vista web, puedes usar la función 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 el script que se va a evaluar no es tan simple y debe usar la entrada de objetos Rust, recomendamos usar el crate serialize-to-javascript.


© 2025 Tauri Contributors. CC-BY / MIT