Electron指南

115 阅读5分钟

Electron笔记

创建集成Vue的Electron项目

方案一: electron-vue 模板项目

方案二: 使用vue-cli创建

  1. 执行以下命令创建项目
vue create my-electron-app
  1. 添加electron-builder插件
vue add electron-builder

相关必要配置项

package.json中的main要配置为background.js

Node相关

针对特定进程的控制流

通过检查 Node.js 的 process.platform 变量,您可以针对特定平台运行特定代码。 请注意,Electron 目前只支持三个平台:win32 (Windows), linux (Linux) 和 darwin (macOS) 。

应用生命周期事件

  1. app.on('ready', callback):当 Electron 完成初始化并准备创建浏览器窗口时触发。
  2. app.on('window-all-closed', callback):当所有窗口都被关闭时触发。
  3. app.on('activate', callback):在 macOS 上,当应用图标被点击而没有打开的窗口时触发。
  4. app.on('browser-window-created', callback):当新的浏览器窗口被创建时触发。

BrowserWindow 类

BrowserWindow类用于创建和管理应用窗口,是 Electron 中最重要的类之一。

const { BrowserWindow } = require('electron');
const mainWindow = new BrowserWindow([options])
options选项:
选项类型描述
widthnumber窗口宽度(像素)
heightnumber窗口高度(像素)
xnumber窗口 x 坐标
ynumber窗口 y 坐标
minWidthnumber窗口最小宽度
minHeightnumber窗口最小高度
maxWidthnumber窗口最大宽度
maxHeightnumber窗口最大高度
titlestring窗口标题
iconstring窗口图标路径
resizableboolean是否可调整大小
movableboolean是否可移动
minimizableboolean是否可最小化
maximizableboolean是否可最大化
fullscreenboolean是否全屏
webPreferencesobjectWebPreferences 配置
窗口操作方法
// 加载URL或文件
mainWindow.loadFile('index.html');
mainWindow.loadURL('https://example.com');
// 窗口状态控制
mainWindow.show();
// 隐藏,非最小化
mainWindow.hide();
mainWindow.focus();
// 最小化
mainWindow.minimize();
mainWindow.maximize();
// 将窗口从最小化状态恢复到以前的状态
mainWindow.restore();
mainWindow.close();
// 获取窗口尺寸和位置
const size = mainWindow.getSize();
const position = mainWindow.getPosition();
// 设置窗口尺寸和位置
mainWindow.setSize(1000, 800);
mainWindow.setPosition(100, 100);

Menu菜单

工具栏菜单

总常用的应该是取消工具栏

win.setMenu(null);

修改原生菜单

  // 创建菜单
  const menu = Menu.buildFromTemplate(menuTemplate);

  // 设置应用菜单
  Menu.setApplicationMenu(menu);
上下文菜单
在主进程中全局配置
  // background.js
  // 创建右键菜单
  const contextMenu = Menu.buildFromTemplate([
    { label: "刷新", role: "reload" },
    { label: "复制", role: "copy" },
    { label: "粘贴", role: "paste" },
  ])

  // 当页面触发 contextmenu 事件时,弹出菜单
  mainWindow.webContents.on("context-menu", () => {
    contextMenu.popup({ window: mainWindow })
  })
(细粒度/组件级):preload.js + IPC + 渲染进程侧调用
// src/App.vue
<template>
    <div id="app" @contextmenu.prevent="showContextMenu">
        <router-view/>
        <input type="text" placeholder="Right-click me too!" />
    </div>
</template>
showContextMenu(event) { 
    // 判断点击的是否為可编辑區域 (如 input, textarea) 
    const isEditable = event.target.tagName === 'INPUT' || event.target.tagName === 'TEXTAREA'; 
    window.electronAPI.showContextMenu(isEditable);
}
// src/preload.js
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
  showContextMenu: (isEditable) => {
    ipcRenderer.send('show-context-menu', isEditable)
  }
})
// background.js
ipcMain.on('show-context-menu', (event, isEditable) => {
  // 根據 isEditable 參數動態建立選單範本
  const template = isEditable ? [] : [];
  const menu = Menu.buildFromTemplate(template)
  menu.popup({ window: BrowserWindow.fromWebContents(event.sender) })
})

// ... 其他程式碼

对话框dialog

文件对话框
const { dialog } = require('electron');

// 打开文件对话框
dialog.showOpenDialog(mainWindow, {
  properties: ['openFile', 'multiSelections'],
  filters: [
    { name: '文本文件', extensions: ['txt'] },
    { name: '所有文件', extensions: ['*'] }
  ]
}).then(result => {
  if (!result.canceled) {
    const filePaths = result.filePaths;
    // 处理选中的文件
  }
});

filters项是文件类型的下拉项,上面代码就是有两个下拉项

消息对话框
const { dialog } = require('electron');

// 消息对话框
dialog.showMessageBox(mainWindow, {
  type: 'info',
  title: '提示',
  message: '这是一个提示消息',
  buttons: ['确定', '取消'],
}).then(result => {
  if (result.response === 0) {
    // 用户点击了确定
  } else {
    // 用户点击了取消
  }
});
系统通知Notification
const { Notification } = require('electron');

// 创建通知
const notification = new Notification({
  title: '通知',
  body: '这是一个系统通知',
  icon: 'path/to/icon.png'
});

// 显示通知
notification.show();

// 处理通知点击事件
notification.on('click', () => {
  // 处理点击事件
});

// 处理通知关闭事件
notification.on('close', () => {
  // 处理关闭事件
});

注意:这个通知是在操作系统的右下角或右上角通知,不是Electron应用内的弹窗!!!

进程间通信(IPC)

概念

IPC:进程间通信 (IPC) 是在 Electron 中构建功能丰富的桌面应用程序的关键部分之一。 由于主进程和渲染器进程在 Electron 的进程模型具有不同的职责,因此 IPC 是执行许多常见任务的唯一方法,例如从 UI 调用原生 API 或从原生菜单触发 Web 内容的更改。

IPC通信的模式

简单口诀send与on,invoke与handle,webContents与on

渲染器进程到主进程(单向)

vue页面调用

window.electronAPI.setTitle()

preload.js预加载脚本中定义context bridge上下文通信桥

const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
  setTitle: (title) => ipcRenderer.send('set-title', title)
})

background.js中通过on语法监听

ipcMain.on('set-title', handleSetTitle)
渲染器进程到主进程(双向)
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('electronAPI', {
  openFile: () => ipcRenderer.invoke('dialog:openFile')
})
const filePath = await window.electronAPI.openFile()
主进程到渲染器进程
mainWindow.webContents.send('update-counter', -1)
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('electronAPI', {
  onUpdateCounter: (callback) => ipcRenderer.on('update-counter', (_event, value) => callback(value))
})

加载预加载脚本后,渲染器进程应有权访问 window.electronAPI.onUpdateCounter() 监听器函数

渲染进程直接调用Electron的API

如果在创建BrowserWindow时,配置了一下四项

      nodeIntegration: true,//node环境继承开启
      contextIsolation: false,//上下文隔离关闭
      webSecurity: false,// 同源策略关闭
      enableRemoteModule: true,// 

那么,渲染器进程可以直接访问Electron的API,无需IPC通信,但这样做存在安全性隐患,故一般直接调用

Tips:渲染进程调用时使用window.require('electron').remote.dialog.showMessageBox,否则可能出现编译错误

clipboard类

clipboard.readText([type])

type string (optional) -可以是 selectionclipboard; 默认为 'clipboard'. selection 仅在 Linux 中可用。

渲染进程使用 Clipboard API和主进程使用clipboard.readText()的区别

clipboard的优势:

  • 不需要用户交互
    • 直接能读取,不受 https 或权限限制。
  • 协议无关
    • 无论渲染进程是 file://http://https:// 都可以工作。
  • 可读写更多格式
    • 除了 text,还能操作 imagehtmlrtf
场景推荐方案
你只需要在前端 Vue 页面里临时读取文本,且不想依赖 Electron 模块navigator.clipboard.readText()
你需要稳定、不依赖安全上下文、无需用户交互,甚至需要读取图片/HTML 等格式electron.clipboard.readText()(主进程或 preload)
需要在渲染进程直接访问系统剪切板在 preload 里通过 contextBridge 暴露 clipboard API 给渲染进程