You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Electron中点击Cancel时showMessageBoxSync弹窗重复出现问题排查

问题根源与解决方案

嘿,我一眼就看出问题出在哪了——你每次点击按钮的时候都在重复注册IPC监听器!这才是弹窗重复、名称重复添加的罪魁祸首。

为什么会出现这个问题?

当你调用window.api.receive()时,底层用的是ipcRenderer.on()方法,这个方法的作用是添加一个永久生效的监听器,每调用一次就会多注册一个。比如:

  • 第一次点击按钮:注册1个getNames和1个getConfirmation监听器,一切正常
  • 第二次点击按钮:又注册1个getNames和1个getConfirmation监听器,现在总共有2组监听器
  • 第三次点击:总共有3组...

后续主进程发送getNames或getConfirmation消息时,所有已注册的监听器都会同时触发——这就是为什么点击Cancel后再触发按钮会弹出多个弹窗,多次点击OK会重复添加名称的原因。


修复方案(两种可选)

方案1:改用一次性监听器(最简单直接)

我们可以在preload.js中新增一个基于ipcRenderer.once()的方法,这个方法注册的监听器触发一次后会自动移除,完美避免重复注册问题。

修改preload.js:

contextBridge.exposeInMainWorld(
  "api",
  {
    send: (channel, data) => {
      let validChannels = ["sendNames"];
      if (validChannels.includes(channel)){
        ipcRenderer.send(channel, data);
      }
    },
    // 新增一次性监听方法
    receiveOnce: (channel, func) => {
      let validChannels = ["getNames", "getConfirmation"];
      if (validChannels.includes(channel)){
        ipcRenderer.once(channel, (event, ...args) => func(...args));
      }
    },
    receive: (channel, func) => {
      let validChannels = ["getNames", "getConfirmation"];
      if (validChannels.includes(channel)){
        ipcRenderer.on(channel, (event, ...args) => func(...args));
      }
    },
    update: (channel, data) => {
      let validChannels = ["updateNames"];
      if(validChannels.includes(channel)){
        ipcRenderer.send(channel, data);
      }
    },
    confirm: (channel, data) => {
      let validChannels = ["confirmName"];
      if(validChannels.includes(channel)){
        ipcRenderer.send(channel, data);
      }
    }
  }
);

然后修改渲染进程的checkName函数,把receive换成receiveOnce:

function checkName(){
  var name = document.getElementById('myInput').value;
  window.api.send("sendNames", "");
  // 改用一次性监听,响应后自动销毁
  window.api.receiveOnce("getNames", (names) => {
    const nameArray = names.split('\r\n');
    if(!nameArray.includes(name)){
      window.api.confirm("confirmName", "This is the first time this name has been used, would you like to add it now?");
      // 同样使用一次性监听
      window.api.receiveOnce("getConfirmation", (result) => {
        if(result == 0) {
          const updatedNames = [...nameArray, name].join('\r\n');
          window.api.update("updateNames", updatedNames);
        }
      });
    }
  });
}

方案2:提前注册永久监听器,用状态变量跟踪请求

如果你更倾向于使用永久监听器,可以把监听器注册移到页面初始化阶段,只注册一次,然后用变量记录当前请求的上下文:

修改渲染进程代码:

// 用变量存储当前请求的上下文
let currentName = "";
let currentNameList = [];

window.onload = function(){
  document.getElementById('myButton').addEventListener('click', checkName);
  
  // 只注册一次getNames监听器
  window.api.receive("getNames", (names) => {
    currentNameList = names.split('\r\n');
    if(!currentNameList.includes(currentName)){
      window.api.confirm("confirmName", "This is the first time this name has been used, would you like to add it now?");
    }
  });
  
  // 只注册一次getConfirmation监听器
  window.api.receive("getConfirmation", (result) => {
    if(result == 0) {
      const updatedNames = [...currentNameList, currentName].join('\r\n');
      window.api.update("updateNames", updatedNames);
    }
    // 重置请求状态
    currentName = "";
    currentNameList = [];
  });
}

function checkName(){
  currentName = document.getElementById('myInput').value;
  window.api.send("sendNames", "");
}

内容的提问来源于stack exchange,提问作者hkr34027

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 11:02:42