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

Electron应用自动更新回调失效问题排查

Electron自动更新回调无法触发问题

问题描述

我正在为Electron应用实现自动更新功能,但回调函数无法正常工作。控制台日志显示showMessage trapped、Checking for Updates以及Preload,说明代码执行到preload函数为止都正常,但回调函数始终无法触发。

关键代码

updater.ts

export default class Updater {
  mainWindow: BrowserWindow;

  constructor(mainWindow: BrowserWindow) {
    this.mainWindow = mainWindow;
  }

  showMessage(message: String) {
    console.log('showMessage trapped');
    console.log(message);
    this.mainWindow.webContents.send('updateMessage', message);
  
  }
}

main.ts

const createWindow = () => {
  mainWindow = new BrowserWindow({
    frame: false,
    show: false,
    width: 400,
    height: 600,
    icon: getAssetPath('icon.png'),
    webPreferences: {
      preload: app.isPackaged
        ? path.join(__dirname, 'preload.js')
        : path.join(__dirname, '../../.erb/dll/preload.js'),
      nodeIntegration: true,
      contextIsolation: true,
    },
  });


  mainWindow.on('closed', () => {
    mainWindow = null;
  });

  mainWindow.webContents.openDevTools({ mode: 'undocked' });
  updater = new Updater(mainWindow);

};


app
  .whenReady()
  .then(() => {
    createWindow();
    app.on('activate', () => {
      if (mainWindow === null) createWindow();
    });

    autoUpdater.checkForUpdates();
    updater?.showMessage('Checking for Updates');
  })
  .catch(console.log);

preload.ts

import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron';
const updateApp = {
  updateMessage: (callback: any) => {
    console.log("Preload");
    ipcRenderer.on('updateMessage', callback);
  },
};


contextBridge.exposeInMainWorld('update', updateApp);

App.tsx

export default function App() {
  let msg:String;
  useEffect(() => {
    window.update.updateMessage(updateMessage);
  }, []);
  const updateMessage = (e: any, message: String) => {
    console.log('updateMessageCallback');
    console.log(message);
    console.log('message logged in view');
  };
  const showPopUp = () => {
    window.my_modal_1.showModal();
  };

  return (
    <div className="min-h-screen flex flex-col">
      <StartingPopUp showPopUp={showPopUp} />
      <TitleBar />
      <UpdateNotification message={msg} />
      <Navbar />
      <Logo />
      <div className="flex flex-col flex-1 p-6">
        <CommandsContainer />
      </div>
      <Progress />
    </div>
  );
}

控制台日志

showMessage trapped
Checking for Updates
Preload

问题分析与解决

问题出在消息发送时机早于渲染进程注册监听,以及回调函数定义顺序问题,具体修复点如下:

  1. 调整消息发送时机,确保渲染进程加载完成
    main.ts中updater?.showMessage是在窗口创建后立即执行的,但此时渲染进程页面还未加载完成,监听事件还没注册。需要等到页面加载完成后再发送消息:
// 修改createWindow函数
const createWindow = () => {
  mainWindow = new BrowserWindow({
    // 原有配置不变
  });

  // 等待页面加载完成后执行更新操作
  mainWindow.webContents.on('did-finish-load', () => {
    autoUpdater.checkForUpdates();
    updater?.showMessage('Checking for Updates');
  });

  mainWindow.on('closed', () => {
    mainWindow = null;
  });

  mainWindow.webContents.openDevTools({ mode: 'undocked' });
  updater = new Updater(mainWindow);
};

// 移除app.whenReady中的autoUpdater和showMessage调用
app
  .whenReady()
  .then(() => {
    createWindow();
    app.on('activate', () => {
      if (mainWindow === null) createWindow();
    });
  })
  .catch(console.log);
  1. 修复React中回调函数的定义顺序
    App.tsx中useEffect调用时,updateMessage函数还未完成初始化,导致传入的是undefined。需要先定义回调函数再注册监听:
export default function App() {
  let msg:String;
  // 先定义回调函数
  const updateMessage = (e: any, message: String) => {
    console.log('updateMessageCallback');
    console.log(message);
    console.log('message logged in view');
  };
  // 再注册监听
  useEffect(() => {
    window.update.updateMessage(updateMessage);
    // 组件卸载时移除监听,避免内存泄漏
    return () => {
      ipcRenderer.removeListener('updateMessage', updateMessage);
    };
  }, []);

  // 其余代码不变
}
  1. 在preload中补充移除监听的方法
    为避免内存泄漏,需要在preload中暴露移除监听的接口:
import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron';
const updateApp = {
  updateMessage: (callback: (event: IpcRendererEvent, message: string) => void) => {
    console.log("Preload");
    ipcRenderer.on('updateMessage', callback);
  },
  removeUpdateListener: (callback: (event: IpcRendererEvent, message: string) => void) => {
    ipcRenderer.removeListener('updateMessage', callback);
  }
};

contextBridge.exposeInMainWorld('update', updateApp);
  1. 改用useState管理消息状态
    原代码中msg是普通变量,React无法监听其变化更新UI,需要替换为useState:
import { useState, useEffect } from 'react';

export default function App() {
  const [msg, setMsg] = useState<string>('');
  const updateMessage = (e: any, message: string) => {
    console.log('updateMessageCallback');
    console.log(message);
    console.log('message logged in view');
    setMsg(message);
  };

  // 其余代码不变
}

完成以上修改后,回调函数即可正常触发,UI也会同步更新消息内容。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 03:25:06