---
title: 不小心写死循环窗口弹出了
abbrlink: 505f6391
date: "2025-09-13 09:10:12"
updated: "2025-09-13 10:19:23"
desc: // 定义一个响应式的引用，用于笔记的文本内容
categories:
    - Tauri
tags:
    - ts
    - vue
    - rust
    - 前端
    - 后端
    - tauri
---

## 关键代码

```ts title="关键前端代码"
// 定义一个响应式的引用，用于笔记的文本内容
const textcontent = ref("");

// 导入必要的Vue组合API函数和Tauri API
import { ref, onMounted, onUnmounted } from "vue";
import { Window } from "@tauri-apps/api/window";
import { invoke } from "@tauri-apps/api/core";

// 从Tauri获取当前窗口实例
const currentWindow = Window.getCurrent();

// 用于保存清理函数的变量
let unlisten: (() => void) | undefined;

// 组件挂载时运行的生命周期钩子
onMounted(async () => {
    // 监听窗口关闭请求事件
    unlisten = await currentWindow.onCloseRequested(async (event) => {
        // 阻止默认关闭行为，以便手动处理
        event.preventDefault();
        // 调用后端命令保存笔记，传递窗口标签和内容
        const shouldClose = await invoke("save_notes", {
            label: currentWindow.label,
            content: textcontent.value,
        });
        // 如果后端指示关闭，则销毁窗口
        if (shouldClose) {
            currentWindow.destroy();
        }
    });
});

// 组件卸载时运行的生命周期钩子
onUnmounted(() => {
    // 如果存在，清理事件监听器
    if (unlisten) {
        unlisten();
    }
});
```

```rust title="关键后端代码"
// 导入必要的Tauri模块，用于处理应用和窗口管理
use tauri::{AppHandle, Manager};
// 从Tauri对话框插件导入对话框扩展和按钮类型
use tauri_plugin_dialog::{DialogExt, MessageDialogButtons};

// 定义一个可以从前端调用的Tauri命令
// 此命令接受窗口标签、笔记内容和应用句柄作为参数
#[tauri::command]
pub fn save_notes(label: &str, content: &str, app: AppHandle) {
    // 打印调试消息，指示特定窗口的保存操作
    println!("nia - saving note for window: {}", label);

    // 检索与给定标签关联的webview窗口
    // 这会解包Option，假设窗口存在（在生产中，应正确处理错误）
    let note_window = app.get_webview_window(label).unwrap();

    // 克隆窗口句柄以在闭包中使用，避免所有权问题
    let note_window_clone = note_window.clone();

    // 在窗口上构建并显示消息对话框
    // 对话框询问用户是否要保存笔记
    let result = note_window
        .dialog()
        .message("Do you want to save the note?")  // 对话框中显示的消息
        .title("Save Note")  // 对话框窗口的标题
        .buttons(MessageDialogButtons::OkCancelCustom(
            "Save".to_string(),  // 确定按钮的自定义文本
            "Cancel".to_string(),  // 取消按钮的自定义文本
        ))
        // 显示对话框并异步处理结果
        .show(move |result| match result {
            // 如果用户点击“保存”（true）
            true => {
                // 打印结果用于调试
                println!("{}", result);
                // 尝试关闭窗口；使用let _忽略任何错误
                let _ = note_window_clone.close();
            }
            // 如果用户点击“取消”（false）
            false => {
                let _ = note_window_clone.close();
            }
        });
}
```

这真是让我自己气笑了——不小心写了个死循环，导致窗口不停弹出对话框，每次都问要不要保存，但无论选什么都会关闭窗口，又触发关闭事件，循环往复。

### 修正

```ts title="修正后的前端代码"
const textcontent = ref("");
import { ref, onMounted, onUnmounted } from "vue";
import { Window } from "@tauri-apps/api/window";
import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event";
interface Event {
    payload: boolean;
}
const currentWindow = Window.getCurrent();
let unlisten: (() => void) | undefined;
let unlistenNoteClose: (() => void) | undefined;
let ifclose = ref<boolean>(false);
onMounted(async () => {
    unlistenNoteClose = await listen("note_close", (event: Event) => {
        ifclose.value = event.payload;
        console.log("saiodiojas  " + ifclose.value);
    });
    unlisten = await currentWindow.onCloseRequested(async (event) => {
        if (!ifclose.value) {
            event.preventDefault();
            const shouldClose = await invoke("save_notes", {
                label: currentWindow.label,
                content: textcontent.value,
            });
        }
    });
});
```

```rs title="修正后的后端代码"
use tauri::{AppHandle, Emitter, Manager};
use tauri_plugin_dialog::{DialogExt, MessageDialogButtons};
#[tauri::command]

pub fn save_notes(label: &str, content: &str, app: AppHandle) {
    println!("nia - saving note for window: {}", label);
    let note_window = app.get_webview_window(label).unwrap();
    let note_window_clone = note_window.clone();
    let app_clone = app.clone();
    let label_clone = label.to_string();
    let result = note_window
        .dialog()
        .message("Do you want to save the note?")
        .title("Save Note")
        .buttons(MessageDialogButtons::OkCancelCustom(
            "Save".to_string(),
            "Cancel".to_string(),
        ))
        .show(move |result| match result {
            true => {
                println!("{}", result);
                app_clone
                    .emit_to(&label_clone, "note_close", result)
                    .unwrap();

                let _ = note_window_clone.close();
            } // do something,
            false => {
                let _ = note_window_clone.close();
            } // do something,
        });
}
```

- **前端修正**：添加了监听 `note_close` 事件的逻辑，使用 `ifclose` 标志来控制是否阻止窗口关闭请求。只有在未收到关闭信号时才阻止默认关闭行为并调用后端保存逻辑，避免了无限循环。

- **后端修正**：在用户选择保存时，通过 `emit_to` 发送事件到前端，设置关闭标志，然后关闭窗口。而在取消时直接关闭窗口，不发送事件，确保不会重复触发关闭请求。

- **整体效果**：这修复了死循环问题，现在保存时窗口正常关闭，取消时也直接关闭，不会反复弹出对话框。😄


---

**作者：**xingwangzhe

**本文链接：**[https://xingwangzhe.fun/posts/505f6391/](https://xingwangzhe.fun/posts/505f6391/)

本文采用[知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-sa/4.0/)进行许可。