electron + vue3 + vite 渲染进程到主进程的双向通信

发布于:2025-03-09 ⋅ 阅读:(15) ⋅ 点赞:(0)

用示例讲解下渲染进程到主进程的双向通信
初始版本项目结构可参考项目:https://github.com/ylpxzx/electron-forge-project/tree/init_project

请添加图片描述

渲染进程到主进程(双向)

以Electron官方文档给出的”渲染进程触发动作,等待主进程返回内容“为例。
Electron的双向通信采用ipcMain.handle(electron主进程内部监听)、ipcRenderer.invoke(包装给前端页面)
实现整项目示例:https://github.com/ylpxzx/electron-forge-project/tree/render_to_main_two_way

ipcMain.handle是 Electron 中用于处理异步 IPC(进程间通信)调用的方法。ipcMain.handle(‘dialog:openFile’, handleFileOpen) 的作用是注册一个处理函数 handleFileOpen,当渲染进程(通常是你的 Vue 应用)发送一个名为 dialog:openFile 的异步消息时,这个处理函数会被调用并返回处理结果。

通信逻辑

  • src/main.js
    在这里插入图片描述
    完整代码如下:

    import { app, BrowserWindow, ipcMain, dialog } from 'electron';
    import path from 'node:path';
    import started from 'electron-squirrel-startup';
    
    // Handle creating/removing shortcuts on Windows when installing/uninstalling.
    if (started) {
      app.quit();
    }
    
    async function handleFileOpen () {
      const { canceled, filePaths } = await dialog.showOpenDialog()
      if (!canceled) {
        return filePaths[0]
      }
    }
    
    const createWindow = () => {
      // Create the browser window.
      const mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
          preload: path.join(__dirname, 'preload.js'),
        },
      });
    
      // and load the index.html of the app.
      if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
        mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
      } else {
        mainWindow.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`));
      }
    
      // Open the DevTools.
      mainWindow.webContents.openDevTools();
    };
    
    app.whenReady().then(() => {
      createWindow();
    
      // Bidirectional communication between rendering process and main process
      ipcMain.handle('dialog:openFile', handleFileOpen)
    
      app.on('activate', () => {
        if (BrowserWindow.getAllWindows().length === 0) {
          createWindow();
        }
      });
    });
    
    app.on('window-all-closed', () => {
      if (process.platform !== 'darwin') {
        app.quit();
      }
    });
    

    在这个示例中,当用户在 Vue 应用中触发一个打开文件对话框事件时,渲染进程会发送一个 ‘dialog:openFile’ 消息到主进程,主进程接收到消息后会调用 handleFileOpen 函数来处理这个请求,并返回处理结果。

  • src/preload.js
    preload.js 用于上下文隔离;将确保您的 预加载脚本Electron的内部逻辑 运行在所加载的webcontent网页之外的另一个独立的上下文环境里。 有助于阻止网站访问Electron 的内部组件和 预加载脚本可访问的高等级权限的API。简而言之就是提供一个入口给渲染进程(前端页面)使用,避免被攻击者随意调用electron内部API。

    const { contextBridge, ipcRenderer } = require('electron/renderer')
    
    contextBridge.exposeInMainWorld('electronAPI', {
      openFile: () => ipcRenderer.invoke('dialog:openFile'),
    })
    

页面示例

通信逻辑实现后,接下来就用一个页面来验证结果

  • src/vue-project/pages/renderToMain/TwoWay.vue

    <template>
      <div>
        <button type="button" @click="onClick" id="btn">Open a File</button>
        <div>
          File path: <strong>{{ inputVal }}</strong>
        </div>
      </div>
    </template>
    
    <script setup>
    import { ref } from 'vue'
    const inputVal = ref('')
    const onClick = async () => {
      // 调用electron对外暴露的openFile API
      const filePath = await electronAPI.openFile()
      inputVal.value = filePath
    }
    </script>
    
  • src/vue-project/router/index.js

    import { createWebHashHistory, createRouter } from 'vue-router'
    
    import HomeView from '@/vue-project/pages/home/index.vue'
    import RenderToMainTwoWay from '@/vue-project/pages/renderToMain/TwoWay.vue'
    
    const routes = [
      { path: '/', component: HomeView },
      // 注册示例路由
      { path: '/renderToMainTwoWay', component: RenderToMainTwoWay },
    ]
    const router = createRouter({
      history: createWebHashHistory(),
      routes,
    })
    
    export default router;
    
  • src/vue-project/App.vue

    <template>
      <h1>🖥️ Hello World!</h1>
      <p>Welcome to your Electron application.</p>
      <p>
        <strong>Current route path:</strong> {{ $route.fullPath }}
      </p>
      <nav>
        <div>
          <RouterLink to="/">Go to Home</RouterLink>
        </div>
        <div>
          <RouterLink to="/renderToMainTwoWay">Render-Process <span style="font-size: 10px;"><--></span> Main-Process
          </RouterLink>
        </div>
      </nav>
      <div style="margin-top: 20px; border: 1px solid grey; padding: 20px; border-radius: 10px;">
        <router-view></router-view>
      </div>
    </template>
    
    <script setup>
    </script>
    

项目结构

在这里插入图片描述