在 Tauri 应用中,当我们需要打开多个窗口(比如主窗口、设置窗口、预览窗口)时,窗口之间的数据传递和状态同步就变得非常重要。Tauri 本身没有提供一个“全局状态管理”来跨窗口自动同步,但提供了几种灵活且可靠的机制,你可以根据场景选用。
1. 前端到 Rust:通过命令(Command)共享数据
每个窗口都可以调用同一个 Rust 后端。因此,最简单的方案是把共享状态放在 Rust 侧(比如一个被 Mutex 或 RwLock 保护的全局结构体)。任何一个窗口通过 invoke 调用命令更新数据后,另一个窗口下次查询时就能拿到最新值。
use std::sync::Mutex;
use tauri::State;
struct AppState(Mutex<String>);
#[tauri::command]
fn set_shared_data(state: State<AppState>, value: String) {
*state.0.lock().unwrap() = value;
}
#[tauri::command]
fn get_shared_data(state: State<AppState>) -> String {
state.0.lock().unwrap().clone()
}
前端窗口 A 调用 invoke('set_shared_data', { value: '新数据' }),窗口 B 调用 invoke('get_shared_data') 就能获取。
优点:实现简单,数据可靠。
缺点:需要主动拉取,无法实时推送变化。
2. Rust 到前端:事件系统(Event)实现实时同步
Tauri 内置了一个发布-订阅的事件系统,可以跨窗口广播数据。后端 Rust 可以向外发送事件,前端任意窗口都可以监听。
Rust 端发送事件:
use tauri::Manager;
// 某个命令中
app_handle.emit_all("data-changed", "新数据").unwrap();
// 或者只发送给指定窗口
window.emit("data-changed", "新数据").unwrap();
前端监听(任何窗口):
import { listen } from '@tauri-apps/api/event';
listen('data-changed', (event) => {
console.log('收到更新:', event.payload);
// 更新本窗口的 UI 状态
});
实际应用:当用户在主窗口修改设置,Rust 处理后向所有窗口广播 settings-updated 事件,设置窗口实时刷新。
优点:实时推送,解耦窗口。
缺点:事件是单向的,需要前端主动监听。
3. 窗口间直接通信:通过 WebView 的 postMessage(受限但可用)
如果你是通过 Tauri 的 WebviewWindow API 创建了新窗口,并且两个窗口加载的是同一个源(比如都是 tauri://localhost),那么你可以用浏览器标准的 window.postMessage 在前端之间传递数据。需要先获取另一个窗口的引用:
主窗口创建子窗口并传递引用:
import { WebviewWindow } from '@tauri-apps/api/webviewWindow';
const sub = new WebviewWindow('sub', { url: '/sub.html' });
// 等子窗口加载完成后传递数据
sub.once('tauri://created', () => {
sub.emit('message', { key: 'value' }); // 这也是事件方式
});
但更推荐使用 Tauri 的事件系统,因为 postMessage 会受到 WebView 同源策略的限制,且管理窗口引用容易出错。
4. 复杂状态同步:结合前端状态管理库
当需要多窗口之间保持复杂的状态树同步(例如 Redux 或 Pinia),可以借助上述的事件系统作为通知管道,让每个窗口独立保持一份本地状态,并在收到变更事件后同步更新。
示例模式:
- 所有修改状态的动作都发给 Rust 统一处理。
- Rust 更新内部状态后,通过
emit_all广播最小化的差异数据。 - 每个窗口的前端监听事件,并更新自己的状态库。
这样可以避免窗口间状态不一致,同时保持性能。
5. 注意事项
- 安全性:不要在前端窗口之间直接传递敏感的未校验数据,所有数据验证和权限检查放在 Rust 层。
- 性能:频繁广播大量数据时,建议只传递必要的差异部分,避免序列化庞大对象拖慢 IPC。
- 窗口生命周期:子窗口关闭时,需要手动清理事件监听器(使用
unlisten),防止内存泄漏。
总结:在 Tauri 中实现多窗口数据同步,短期简单用事件推送,长期可靠性用 Rust 中央状态 + 事件广播。这种模式既保持了前端开发的灵活性,又利用 Rust 保证了数据的安全和一致。