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

Electron TS React应用渲染进程卡顿问题及优化方案咨询

问题:Electron主进程API请求导致渲染进程冻结

我正在开发一个基于Electron+TypeScript+React的账户管理器应用,功能是获取指定账户的数据并展示给用户。目前遇到的问题是:当ipcMain监听acc:reload事件时,主进程从API获取数据、存入对象并发送给渲染进程的过程中,渲染进程会在整个请求时长(多账户时为累计请求时长)内冻结。我原本以为主进程与渲染进程相互独立,不会造成渲染阻塞。

相关代码

listeners.ts(片段)

ipcMain.on('acc:reload', async (event) => {
    event.reply('acc:reload', accounts); // old account data to at least display something
    try {
      const updatedAccounts = await Promise.all(
        accounts.map(async (acc) => {
          try {
            const newData = await getAccountData(acc);
            return { ...acc, data: newData };
          } catch (error) {
            console.error(
              `Error reloading data for account ${acc.username}:`,
              error
            );
            // If an error occurs, return the original account without data
            return acc;
          }
        })
      );
      writeAccountsToFile(updatedAccounts);
      event.reply('acc:reload', updatedAccounts); // new 'fetched' account data
    } catch (error) {
      console.error('Error reloading account data:', error);
    }
  });

App.tsx

function Main() {
  const refreshAccounts = () => {
    window.electron.accountLoadHandler.sendMessage('acc:reload');
  };
  useEffect(() => {
    refreshAccounts();
  }, []);
  window.electron.accountLoadHandler.once('acc:reload', (accs: Account[]) => {
    setAccounts(accs);
  });

  return (
    <div className="px-4 dark:bg-zinc-900 h-full min-h-screen dark:text-zinc-300">
      <Header handleRefresh={refreshAccounts} switchDark={switchDark} />
      <AccountList accounts={accounts} />
      <ToastContainer />
      <div className="flex w-full p-2 justify-center">
        <p>2024 ©</p>
      </div>
    </div>
  );
}

账户数据获取函数

export default async function getAccountData(acc: Account) {
  const url = `example.com/api/data` + account.name;
  try {
    const response = await fetch(url);
    const text = await response.text();
    const dom = new JSDOM(text);
    const flexData = await readFlexData(dom.window.document);
    const soloData = await readSoloData(dom.window.document);

    const data = {
      solo: soloData,
      flex: flexData,
    } as IData;
    return data;
  } catch (err) {
    console.error('Request failed ERROR:', err);
    return undefined;
  }
}

方案咨询

我想咨询以下两种方案是否符合最佳实践:

  • 在渲染进程中发起请求,将数据发送到主进程进行修改、清洗、写入文件后,再返回给渲染进程;
  • 使用Worker Thread处理相关逻辑。

专业建议

阻塞原因分析

问题核心不是IPC通信,而是主进程中的JSDOM解析属于CPU密集型任务,会阻塞主进程的单线程事件循环。主进程被阻塞时,无法及时响应渲染进程的IPC消息、窗口交互事件,最终导致渲染进程冻结。

方案评估

  1. 渲染进程发起请求的方案
    不推荐。渲染进程发起网络请求本身可行,但数据清洗(尤其是JSDOM解析)如果放回主进程,依然会阻塞主进程;如果放在渲染进程执行,CPU密集型任务会直接卡住UI。另外,账户敏感凭证若放在渲染进程,存在安全风险,不符合Electron安全规范。

  2. 使用Worker Thread处理逻辑
    这是更合理的方案。将API请求、JSDOM解析、数据提取这些CPU或IO密集型任务放到Worker Thread中执行,主进程仅负责调度Worker、处理IPC通信和文件写入,避免阻塞主进程事件循环,渲染进程就能保持响应。
    具体实现步骤:

    • 主进程收到acc:reload事件后,立即返回旧账户数据给渲染进程;
    • 将账户列表发送给Worker Thread,由Worker并行处理每个账户的数据获取与解析;
    • Worker完成所有任务后,将结果返回主进程,主进程执行文件写入,再把更新后的账户数据发送给渲染进程。

额外优化点

  • 替换once为on监听acc:reload事件,确保多次刷新都能触发数据更新;
  • 给每个账户添加独立的加载状态提示,提升用户体验;
  • 使用fs.promises.writeFile异步写入文件,避免同步IO操作阻塞主进程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 02:15:18