当前位置: 首页 > article >正文

分享electron多窗口实践

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

创建并打开新窗口

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

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

window2.vue

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

preload.js

const { contextBridge, ipcRenderer } = require('electron')

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

main.js

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


http://www.kler.cn/a/369399.html

相关文章:

  • 重写(外壳不变)
  • Ovis原理解读: 多模态大语言模型的结构嵌入对齐
  • Unity编辑器 连接不到SteamVR问题记录
  • DO・PO・DTO・BO・AO・VO
  • 【modbus协议】libmodbus库移植基于linux平台
  • 介绍 Docker 的基本概念和优势,以及在应用程序开发中的实际应用。(AI)
  • 使用 NLP 和模式匹配检测、评估和编辑日志中的个人身份信息 - 第 2 部分
  • WPF入门_06资源和样式
  • 在Guided模式下给无人机发送命令设置位置速度
  • 1553B总线电缆网络测试及数据分析设备
  • Linux iptables基本使用
  • RHCE 第二次作业
  • Aloudata BIG 主动元数据平台支持 Oracle/DB2 存储过程算子级血缘解析
  • Django 获取用户IP
  • #Swift The difference between Parameter and Agrument
  • 第十八届联合国世界旅游组织/亚太旅游协会旅游趋势与展望大会在广西桂林开幕
  • 智慧工地:建筑热潮退去后的挑战与应对策略
  • pandas习题 043:移动窗口中最大值对应的其他列值
  • 期刊论文写作之LATEX模板(持续更新……)
  • php反序列化漏洞典型例题
  • 时间序列预测(十五)——有关Python项目框架的实例分析
  • Merlion笔记(四):添加一个新的预测模型
  • map 和 set 的使用
  • 【skywalking】linux centos7安装skywalking 10.1.0
  • 设计图实时备份软件免费
  • 如何将Windows server 2003从本地服务器变成域成员服务器