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

如何在Electron主进程就绪后执行单例文件中的函数

解决Electron+Vue中globals.ts过早调用ipcRenderer的问题

问题核心是你在globals.ts里用了立即执行的异步函数,模块加载时就会触发IPC调用,但此时Electron主进程的IPC处理器还没完成注册,或者渲染进程与主进程的通信通道还没建立。以下是几种可靠的解决方式:


方案1:将地址获取逻辑封装为异步函数,延迟调用

把address从立即执行的Promise改成可手动调用的异步函数,在Vue组件的生命周期钩子中(确保渲染进程就绪后)再执行:

修改src/assets/globals.ts

export async function getAddress(): Promise<string> {
  try {
    const hostname: string = await window.ipcRenderer.invoke("getMachineIPv4", "please work");
    const firstThreeNumbers = hostname.split(".")[0];
    const portNumber = "3000";
    const subnet = "10";
    
    if (firstThreeNumbers === "123") {
      return `123.123.1111.${subnet}:${portNumber}`;
    } else if (firstThreeNumbers === "101") {
      return `123.123.111.${subnet}:${portNumber}`;
    } else if (hostname === "localhost") {
      return `123.123.111.${subnet}:${portNumber}`;
    } else {
      console.error("ip地址未找到");
      return "error";
    }
  } catch (error) {
    console.error("获取地址失败:", error);
    return "error";
  }
}

在Vue组件中调用(以App.vue为例)

<script setup lang="ts">
import { getAddress } from './assets/globals';
import { onMounted, ref } from 'vue';

const address = ref<string>("加载中...");

onMounted(async () => {
  // 组件挂载后再调用,此时渲染进程与主进程通信已就绪
  address.value = await getAddress();
});
</script>

<template>
  <div>{{ address }}</div>
</template>

方案2:确保主进程IPC处理器先注册,再创建窗口

调整主进程代码顺序,先完成IPC处理器的注册,再执行窗口创建逻辑,避免窗口加载时主进程还没准备好响应IPC调用:

修改electron/main/main.ts

import { app, BrowserWindow, ipcMain } from 'electron';
import os from 'os';
import path from 'path';

// 先注册IPC处理函数,再创建窗口
ipcMain.handle("getMachineIPv4", (e, arg) => {
  console.log("获取ipv4");
  const interfaces = os.networkInterfaces();
  for (const interfaceName in interfaces) {
    const interfaceInfo = interfaces[interfaceName];
    for (const info of interfaceInfo) {
      if (info.family === "IPv4" && !info.internal) {
        return info.address;
      }
    }
  }
  // 未找到有效IP时返回localhost作为兜底
  return "localhost";
});

async function createWindow() {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, '../preload/preload.js')
    }
  });

  await mainWindow.loadURL(process.env.VITE_DEV_SERVER_URL || `file://${path.join(__dirname, '../../dist/index.html')}`);
}

app.whenReady().then(() => {
  createWindow();
  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit();
});

方案3:通过主进程就绪信号触发IPC调用

在预加载脚本中添加等待主进程就绪的方法,主进程窗口加载完成后主动发送就绪信号,渲染进程收到信号后再执行IPC调用:

修改electron/preload/preload.ts

import { contextBridge, ipcRenderer } from 'electron';

contextBridge.exposeInMainWorld("ipcRenderer", {
  send: (channel: string, data: string) => {
    const validChannels = ["OpenFolder"];
    if (validChannels.includes(channel)) {
      ipcRenderer.send(channel, data);
    }
  },
  receive: (channel, func) => {
    const validChannels = ["fromMain"];
    if (validChannels.includes(channel)) {
      ipcRenderer.on(channel, (event, ...args) => func(...args));
    }
  },
  invoke: (channel, arg) => {
    console.log(`${channel} 被调用`);
    const validChannels = ["asyncFunc", "getMachineIPv4"];
    if (validChannels.includes(channel)) {
      return ipcRenderer.invoke(channel, arg);
    }
    return Promise.reject(new Error("无效的IPC通道"));
  },
  // 添加等待主进程就绪的方法
  waitForMainReady: () => {
    return new Promise<void>((resolve) => {
      ipcRenderer.once('main-ready', () => resolve());
    });
  }
});

修改electron/main/main.ts的createWindow函数

async function createWindow() {
  const mainWindow = new BrowserWindow({
    // 窗口配置...
    webPreferences: {
      preload: path.join(__dirname, '../preload/preload.js')
    }
  });

  await mainWindow.loadURL('你的Vue应用地址');

  // 窗口加载完成后,通知渲染进程主进程已就绪
  mainWindow.webContents.on('did-finish-load', () => {
    mainWindow.webContents.send('main-ready');
  });
}

修改src/assets/globals.ts

export async function getAddress(): Promise<string> {
  // 先等待主进程就绪信号
  await window.ipcRenderer.waitForMainReady();
  
  try {
    const hostname: string = await window.ipcRenderer.invoke("getMachineIPv4", "please work");
    // 后续逻辑不变
  } catch (error) {
    console.error("获取地址失败:", error);
    return "error";
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 23:12:54