分享electron多窗口实践

在当今的软件开发领域,Electron框架以其独特的能力脱颖而出,它允许开发者使用Web技术构建跨平台的桌面应用程序。随着Electron应用的复杂性增加,多窗口管理和窗口间通信的需求也日益增长。本文将深入探讨如何在Electron中创建和管理多个窗口,并实现它们之间的有效通信。我们将从基础概念出发,逐步深入到实际代码实现,确保您能够理解并应用这些技术,以构建更加丰富和互动的桌面应用体验。让我们开始这段技术探索之旅,揭开Electron多窗口应用开发的神秘面纱。

创建并打开新窗口

javascript 复制代码
const { BrowserWindow, app, ipcMain} = require('electron')
const electron = require('electron')
const url = require('url')
const path = require('path')

var window2 = null
exports.getWindow = () => {
  return window2
}
exports.launch = () => {
  /// 实现在特定窗口打开新窗口

  // id:与显示器相关联的唯一标识符(Integer)。
  // rotation:屏幕旋转的角度,可以是 0, 1, 2, 3,分别代表 0°, 90°, 180°, 270°(Integer)。
  // scaleFactor:输出设备的像素比例因子(Number)。
  // touchSupport:是否支持触摸,可能的值为 available, unavailable, unknown(String)。
  // bounds:包含显示器的边界矩形(Object)。
  // size:包含显示器的宽度和高度(Object)。
  // workArea:包含显示器的工作区域矩形(Object)。
  // workAreaSize:包含显示器工作区域的宽度和高度(Object)。
  var displays = electron.screen.getAllDisplays()
  // 主要使用到bounds 中的x和y参数
  // x:显示器左上角的横坐标(水平位置),以像素为单位。
  // y:显示器左上角的纵坐标(垂直位置),以像素为单位。x
  // 这些属性共同定义了显示器在屏幕上的确切位置和尺寸。例如,如果你有两个横向并排的显示器,主显示器(通常是0号显示器)的 bounds 可能看起来像这样:
  // {
  //   x: 0,
  //   y: 0,
  //   width: 1920,
  //   height: 1080
  // }
  // 第二个显示器(1号显示器)的 bounds 可能看起来像这样:
  // {
  //   x: 1920, // 假设两个显示器宽度都是1920像素
  //   y: 0,
  //   width: 1920,
  //   height: 1080
  // }
  var externalDisplay = displays.find((display) => {
    return display.bounds.x !== 0 || display.bounds.y !== 0
  })
  /// 用x和y 确保窗口在第二个屏幕中打开
  var x = externalDisplay ? externalDisplay.bounds.x + 10 : 0
  var y = externalDisplay ? externalDisplay.bounds.y + 10 : 0
  const windowOptions = {
    width: 1000,
    height: 1000,
    x: x,
    y: y
  }
  window2 = new BrowserWindow(windowOptions)
  if (global.debug) {
    window2.loadURL('http://localhost:8080/#/window2')
  } else {
    // 生产模式中,path.join('file://', __dirname, '../dist/index.html#/window2?query=xx') 指定路由不生效,携带的参数也会丢失,统一重定向到首页。
    // 故使用以下方式构建路由
    const cUrl = url.format({
      protocol: 'file', // 指定 URL 的协议部分。在这个例子中,protocol 被设置为 'file',表示这是一个文件 URL
      slashes: true, // 指示在协议之后是否添加双斜杠(//)
      pathname: path.join(__dirname, '../dist/index.html'), // 指定 URL 的路径部分
      hash: 'window2', // 指定 URL 的片段标识符(hash),也就是 URL 中 # 后面的部分
      query: {
        query: 'xx'
      }
    })
    window2.loadURL(cUrl) // 将url加载到window2的实例中
  }
  window2.show() // 显示窗口
}

窗口间的通信

由于没有直接的方法可以使用ipcMain和ipcRender模块在Electron中的渲染进 程之间发送消息。electron官方推荐2种方案进行进程间的通信

  • 将主进程作为渲染器之间的消息代理。 这需要将消息从一个渲染器发送到主进程,然后主进程将消息转发到另一个渲染器。
    • 优点
      • 安全性:由于渲染进程之间的通信需要通过主进程,这样可以更好地控制和监控跨渲染进程的通信,提高安全性。
      • 控制性:主进程可以对消息进行审核和过滤,避免潜在的不安全操作或者错误数据的传递
    • 缺点
      • 性能开销:每次渲染进程之间的通信都需要经过主进程,这会增加通信的延迟和性能开销
      • 复杂性:增加了主进程的负担,需要处理更多的消息转发逻辑,可能导致代码复杂度增加
  • 从主进程将一个 MessagePort 传递到两个渲染器。 这将允许在初始设置后渲染器之间直接进行通信。
    • 优点
      • 直接通信:渲染进程之间可以直接通过 MessagePort 进行通信,减少了通信的中间环节,提高了效率。
      • 减少主进程负担:不需要主进程介入每次通信,减轻了主进程的负担,降低了主进程的复杂性。
    • 缺点
      • 安全性降低:直接通信可能会增加安全风险,因为主进程无法监控和控制渲染进程之间的通信内容。
      • 调试难度:如果渲染进程之间的通信出现问题,调试可能会更加困难,因为没有主进程作为中间人来记录和监控通信过程。

由于业务对安全性的要求,故选择使用第一种主进程代理的方案实现渲染进程间的通信。以下是具体实现

window1.vue

javascript 复制代码
function sendMessage () {
  window.electronAPI.sendMessage(message) // 窗口1向主进程发送信息
}

window2.vue

javascript 复制代码
const ipc = window.require('electron').ipcRenderer
export default {
  // ...
  created () {
    // 窗口2监听来自主线程的主屏信息
    ipc.on(
      'message-from-window1',
      (message) => {
        // 获取到了主屏发送的message
      }
    )
  }
}

preload.js

javascript 复制代码
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('electronAPI', {
  sendMessage: (message) => ipcRenderer.send('send-message', message) // 暴露sendMessage方法
})

main.js

javascript 复制代码
const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('node:path')
const window2 = require('./window2')
// ...

function handleSendMessage (event, message) {
  window2.webContents.send('message-from-window1', message) // 主线程发送主屏信息给窗口2
}

function createWindow () {
  const Window1 = new BrowserWindow({
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })
  Window1.loadFile('index.html')
}

app.whenReady().then(() => {
  ipcMain.on('send-message', handleSendMessage) // 主线程监听并接收主屏信息
  createWindow()
})

-from manta

相关推荐
LvManBa11 小时前
Vue学习记录之二十二 Vue3+vite+electron 构建项目实例
vue.js·学习·electron
itas1092 天前
Electron调用nodejs的cpp .node扩展【非安全】
electron·nodejs·addon·electron c++·electron cpp
蓝胖子不是胖子2 天前
electron的常用api
前端·javascript·electron
loriloy3 天前
Electron 离线环境打包解决方案(electron-forge)
electron·1024程序员节
itas1095 天前
Electron调用nodejs的cpp .node扩展【安全】
electron·nodejs·electron c++·electron addon·electron c++插件
zhbitxhd6 天前
electron如何和cocos通信
前端·javascript·electron
程序员爱技术8 天前
Electron兼容win7版本的打包流程
前端·javascript·electron
Yogurt_cry9 天前
[个人项目] 用 1 个多月彻底解决困扰近 2 年的麻烦事——付款跟单管理工具
前端·vue.js·electron
海威的技术博客12 天前
Electron+Vue实现两种方式的截屏功能
javascript·vue.js·electron