Emitir: a todos, a uno, o a los que cumplan
Las tres formas de dirigir un evento y qué llega a quién. El nombre como contrato, la carga útil y su forma, y la regla que evita que una ventana repinte dos veces.
Las tres formas
// 1 · a todos: cualquier ventana que escucheapp.emit("catalogo-actualizado", &resumen)?;// 2 · a una concreta, por su etiquetaapp.emit_to("main", "indexado-progreso", (hechos, total))?;// 3 · a los que cumplan una condiciónapp.emit_filter("proyecto-cambiado", &resumen, |target| { // por ejemplo: todas menos la que originó el cambio !matches!(target, tauri::EventTarget::WebviewWindow { label } if label == origen)})?;Y desde una ventana, window.emit(...) emite con esa ventana como origen, que es lo que el nivel 16.4 usaba para dirigir el progreso a quien lo pidió.
El nombre es un contrato, y es una cadena
Aquí vuelve el problema del nivel 14.3 con otra cara: el nombre del evento es una cadena en los dos lados y nada comprueba que coincidan. Los bindings del nivel 19 pueden generar los tipos de la carga útil, y el nombre sigue siendo texto que hay que escribir igual dos veces.
Tres reglas que lo hacen soportable:
Constantes, no literales. En Rust, un módulo con los nombres; en el frontend, un objeto congelado. Un error de escritura deja de compilar en el lado donde ocurra.
pub mod eventos { pub const CATALOGO_ACTUALIZADO: &str = "catalogo-actualizado"; pub const INDEXADO_PROGRESO: &str = "indexado-progreso";}Una convención de forma. Minúsculas con guiones, sin acentos, y con la estructura sujeto-verbo: proyecto-guardado, catalogo-actualizado, indexado-progreso. Así la lista se lee y se ordena sola.
Prefijo cuando puede colisionar. Los plugins emiten sus propios eventos, y algunos nombres genéricos —update, error, ready— pueden chocar. Un prefijo de tu aplicación lo evita de raíz.
La carga útil
Se serializa igual que todo lo demás, con las reglas del nivel 17. Y hay tres decisiones propias:
Manda lo que la vista necesita, no el objeto entero. Es el criterio del nivel 15.3 otra vez: un evento que lleva el proyecto completo con su contenido se emite a todas las ventanas y se serializa una vez por cada una.
Manda el identificador, siempre. Aunque el evento sea «algo cambió», el receptor necesita saber qué. Un evento sin identificador obliga a recargar todo, que es justo lo que se quería evitar.
Considera mandar solo el identificador. Para datos grandes o que cambian mucho, el patrón «te aviso de qué cambió y tú pides lo que necesites» reparte mejor: el evento es diminuto y solo las vistas interesadas pagan el cruce de los datos.
// pequeño y suficienteapp.emit(eventos::PROYECTO_CAMBIADO, &ProyectoResumen::de(p))?;// o mínimo, si el resumen es grande y pocos lo necesitanapp.emit(eventos::PROYECTO_CAMBIADO, &p.id)?;La regla del repintado doble
Ya apareció en el nivel 15.5 y aquí es donde se implementa. Si un comando muta y devuelve el estado nuevo, quien llamó ya lo tiene; si además le llega el evento, repinta dos veces.
Tres formas de evitarlo, de peor a mejor:
- El frontend ignora el evento si el identificador coincide con lo que acaba de pedir. Funciona y reparte la lógica en el sitio equivocado.
- El evento lleva la etiqueta de la ventana origen y cada receptor decide. Mejor, y sigue exigiendo que todos se acuerden.
- El core no se lo manda. Con el filtro de arriba, o recorriendo las ventanas y saltándose la origen. Es la única que no depende de que nadie se acuerde.