Electron介绍
Electron 是由Github开发,现在由OpenJS基金会维护的一个开源框架,它允许开发者使用web技术构建跨平台桌面应用。
Electron 的核心组成是 Chromium、Node.js 以及内置的 Native API。
什么时候用Electron
Electron谁在用
Electron 的架构原理
Chromium架构
Chromium 的本质是 Chrome 的开源版,也是一个浏览器,浏览器也是一个桌面应用,它需要去创建窗口,右键菜单,管理浏览器 Tab 页面还有扩展程序等,处理这些事项的进程为主进程,即下图 Browser 部分。而对应每个具体页面的进程,我们称它为渲染进程,即下图 Render 部分。两个进程需要通信的话,就需要跨进程通信,即 IPC。
这个图中,我们可以看出:
Electron 架构
由于 Electron 使用了 Chromium 来展示 Web 页面,所以 Chromium 的多进程架构,也会被使用到 Electron 中,在 Electron 中也分为主进程,渲染进程等。但是跟 Chromium 不一样的有两点,一个是 Electron 在各个进程中暴露了一些 Native API,第二是 Electron 引入了 Node.js。在一个主线程中,同一个时间下只能运行一个事件循环,但是 Node.js 的事件循环是基于 libuv,而 Chromium 的事件循环是基于 message bump,这就是 Electron 原理的重点,即如何整合事件循环,主要的思路有两种:1)将 Chromium 的 message bump 用 libuv 实现一次, 比如 NW,2)将 Node.js 集成到 Chromium,Electron 采用第二种思路实现。
Electron 安装
新建项目中安装electron
npm install electron --save-dev
安装报以下错误的话,可以尝试使用以下方式:
export ELECTRON_MIRROR=http://npm.taobao.org/mirrors/electron/
npm config set registry http://registry.npm.taobao.org/
npm install electron --save-dev
Electron 主进程模块
Electron 运行 package.json 的 main 脚本的进程被称为主进程。
每个应用只有一个主进程,主进程管理原生 GUI,创建渲染进程,控制应用的生命周期。
- app 用于控制应用程序的生命周期
- BrowserWindow,用于创建和控制应用窗口
在 Electron 中,只有在 app 模块的ready
事件触发后才能创建 BrowserWindows 实例。 您可以通过使用app.whenReady()
API 来监听此事件,并在其成功后调用createWindow()
方法。
// 创建窗口,并设置宽高
let win = new BrowserWindow({ width, height ...})
// 加载页面
win.loadURL(url)
win.loadFile(path)
- Notification,创建一个可交互的通知
let notification = new Notification({title, body, actions:[{ text, type }]})
notification.show()
- ipcMain 是跟 ipcRenderer 进行 IPC 通信的。ipcMain.handle(channel, handler), 处理渲染进程的 channel 请求,在 handler 中 return 返回结果
- webContents 用来加载具体的页面
- autoUpdater,更新模块
- globalShortcut,用来设置全局快捷键
- clipboard,用来读写剪切板
- crashReporter,用来监控主进程和渲染进程是否有崩溃
Electron 渲染进程
展示 Web 页面的进程称为渲染进程。通过 Node.js、 Electron 提供的 API 可以跟系统底层打交道,一个 Electron 可以有多个渲染进程。
- ipcRenderer
- remote,可以调用主进程的模块
- desktopCapture,用来捕获桌面流
Electron进程间通信
进程间通信的目的:
- 通知事件
- 数据传输
- 共享数据
可以使用 Electron 的 ipcMain
模块和 ipcRenderer
模块来进行进程间通信。ipcMain
和 ipcRenderer
都是 EventEmitter 对象。
从渲染进程到主进程通信
- Callback 写法
// 渲染进程通过 ipcRenderer.send() 向主进程发送事件
ipcRenderer.send(channel, ...args)
// 主进程通过 ipcMain.on() 来接收并且响应事件
ipcMain.on(channel, handler)
- Promise 写法(Electron7.0 之后,处理请求+响应模式),这种模式下,最好自定义超时限制
// 渲染进程通过 ipcRenderer.invoke() 向主进程发送事件
ipcRenderer.invoke(channel, ...args)
// 主进程通过 ipcMain.handle() 来接收并且响应事件
ipcMain.handle(channel, handler)
主进程通知渲染进程
主进程中使用 webContents 的 send 方法去发送事件给渲染进程
webContents.send(channel)
渲染进程中,使用 ipcRenderer.on(channel, handler) 来响应事件
渲染进程之间的通信(页面间的通信)
- 通知事件
- 通过主进程转发(Electron 5 之前)
- ipcRenderer.sendTo(webContentsId, channel, ...args), webContentsId 为要发送到的窗口Id
// 进程1向进程2发送事件 ipcRenderer.sendTo(webContentsId, channel, ...args) // 进程2接收并响应进程1的事件 ipcRenderer.on(channel, handle)
- 数据共享
- web 技术:localStorage、sessionStorage、indexedDB
- remote,会将数据挂载在一个全局的过程中,如果用不好,会造成程序卡顿,影响性能,所以尽量减少使用
Electron 应用原生能力
使用 Electron API 创建原生 GUI
- BrowserWindow 应用窗口
- Tray 托盘
- app 设置 dock.badge
- Menu 菜单
- dialog 原生弹窗
- TouchBar 苹果触控栏
使用 Electron API 获得底层能力
- clipboard 剪切板
- globalShortcut 全局快捷键
- desktopCapture捕获桌面
- shell 打开文件、URL等
webRTC
如何捕获桌面流/窗口流
desktopCapturer.getSources
({types:["window", "screen"]}), 提取 chromeMediaSourceId
- Electron