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
相关产品推荐
相关产品推荐

