图纸章的最后一刀是通电:从双击图标到进程退出,Tauri 按什么时序驱动你的代码。本节给出完整的生命周期地图,并回答一个实操问题——初始化数据库、恢复窗口状态、弹出确认框这些代码,各自该挂在哪个钩子上。挂错时序的初始化,是"启动偶尔白屏""退出不弹确认"这类疑难杂症的根源。
状态图上有两个"你的代码可以进场"的窗口:Setup 钩子(页面加载前)与运行中(命令、事件、窗口事件回调里)。大多数初始化错误就是把该在 Setup 做的事放到了运行中,或反之。
pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_store::Builder::new().build()) .invoke_handler(tauri::generate_handler![add_note, list_notes]) .setup(|app| { // 此时窗口已按配置创建,但页面未必加载完 if let Some(main) = app.get_webview_window("main") { let _ = main.set_title("随身笔记"); } // 后台任务不阻塞启动:交给异步运行时 tauri::async_runtime::spawn(async move { if let Err(e) = open_database().await { eprintln!("数据库初始化失败: {e}"); } }); Ok(()) }) .run(tauri::generate_context!()) .expect("failed to run"); }
Setup 里做三件事最合适:同步初始化(注册菜单、恢复窗口状态插件的数据)、改窗口属性(标题、尺寸微调)、派发后台任务(数据库连接、文件索引——注意用 async_runtime::spawn 扔给运行时,不要在 Setup 里直接 await,否则窗口迟迟不出来)。反过来,别在 Setup 里做重活是启动性能的第一守则,第 8 章会量化这件事。
页面加载完成后进入稳态,命令与事件按 2.2 节的传送带往返。这一阶段还有一个容易被忽视的事件源:窗口事件。用户拖动、缩放、聚焦、点关闭按钮,都从这里来:
.on_window_event(|window, event| match event { tauri::WindowEvent::CloseRequested { api, .. } => { // 有未保存内容则拦截关闭,改为询问 if has_unsaved(window.label()) { api.prevent_close(); confirm_exit(window.app_handle(), window.label()); } } tauri::WindowEvent::Resized { .. } => { // 防抖后持久化窗口尺寸 schedule_save_window_state(window.label()); } _ => {} })
CloseRequested 是"关闭拦截"的标准挂点:prevent_close 让本次关闭作废,应用转入询问流程——弹原生对话框或发事件让前端弹自己的保存确认 UI。托盘常驻类应用在这里分流出"关闭窗口但留在托盘"的语义(第 7 章托盘节有完整实现)。
所有窗口都关了,进程走到退出。想在退出前清理(保存设置、关闭数据库连接、给更新检查留遗言),挂 RunEvent::ExitRequested 或 Exit:
.build(tauri::generate_context!()) .expect("failed to build") .run(|app, event| match event { tauri::RunEvent::ExitRequested { code, .. } => { if code.is_none() { // code 为空表示还有窗口在阻止退出,可在此决定是否强制 } flush_pending_writes(); } tauri::RunEvent::Exit { .. } => { // 最后的清理点,尽量只做快操作 close_database(); } _ => {} })
退出清理的两条纪律:只做快操作——退出路径上的用户耐心以毫秒计,慢活应该在运行中增量完成;幂等——崩溃场景下清理函数可能没机会执行,所以运行中就要把关键数据落盘,退出清理只是兜底而不是唯一保障。
遇到"启动白屏""初始化偶发失败",按三层顺序排查:前端在 onLoad 里 invoke 的命令,如果该命令依赖 Setup 里初始化的状态,就是时序倒挂——要么把初始化挪进 Setup 同步段,要么命令侧返回"未就绪"让前端重试。窗口事件没触发,先确认 Builder 链上挂了 on_window_event 且在最外层 run 上(而不是某个窗口对象上)注册。记住原则:Setup 里同步可慢则派发、前端入口不假设芯已就绪、退出清理幂等——三条覆盖九成生命周期坑。
CloseRequested 的 prevent_close,托盘常驻应用在此分流;第 2 章图纸收卷:三层、传送带、壳体差异、时序全图都在手上。下一章开工备料——把环境装齐,把脚手架跑通,把装配说明书逐项读懂。