Electron 提供了一种基于Chrome内核的跨平台桌面APP方案。通过 Electron 可以使用 JavaScript、HTML、CSS 等 Web 技术创建原生应用程序框架。Electron 主要解决了 web 和客户端的交互方案,并提供了大量常用的客户端组件。通过主进程和渲染进程分别管理两端的内容。

基本应用

从开发的角度来看, Electron application 本质上是一个 Node. js 应用程序。 与 Node.js 模块相同,应用的入口是 package.json 文件。 一个最基本的 Electron 应用一般来说会有如下的目录结构:

  1. your-app/
  2. ├── package.json
  3. ├── main.js
  4. └── index.html

快速创建一个 Electron 应用:

  • 安装 electron
  1. npm init
  2. npm install --save-dev electron

修改 package.json 文件,指定 main.js 文件,设置启动命令:

  1. {
  2. "name": "your-app",
  3. "version": "0.1.0",
  4. "main": "main.js",
  5. "scripts": {
  6. "start": "electron ."
  7. }
  8. }
  • 实现 main.js 文件
  1. const { app, BrowserWindow } = require('electron')
  2. function createWindow () {
  3. // 创建浏览器窗口
  4. const win = new BrowserWindow({
  5. width: 800,
  6. height: 600,
  7. webPreferences: {
  8. nodeIntegration: true
  9. }
  10. })
  11. // 并且为你的应用加载index.html
  12. win.loadFile('index.html')
  13. }
  14. app.whenReady().then(createWindow)
  • 实现 HTML 文件
  1. <html>
  2. <head>
  3. <meta charset="UTF-8">
  4. <title>Hello World!</title>
  5. </head>
  6. <body>
  7. <h1>Hello World!</h1>
  8. We are using node <script>document.write(process.versions.node)</script>,
  9. Chrome <script>document.write(process.versions.chrome)</script>,
  10. and Electron <script>document.write(process.versions.electron)</script>.
  11. </body>
  12. </html>
  • 执行 npm run start 启动应用

也可以使用 Electron-vue 脚手架创建基于 vue cli 的应用

通信

混合应用的基本问题之一通信

Electron 提供了 ipcMain 模块和 ipcRender 模块基于事件监听模式来实现客户端和web 页面的通信。

基本逻辑是在主线程中使用 ipcMain 监听页面的事件请求,并基于event 返回请求结果。事件的基本里流程即 进程1 发起请求-》进程2 响应请求

请求的事件分为同步事件、异步事件,监听的事件分为全局监听和一次性监听。具体实现如下:

主线程

  1. // In main process.
  2. const ipcMain = require('electron').ipcMain;
  3. ipcMain.on('asynchronous-message', function(event, arg) {
  4. console.log(arg); // prints "ping"
  5. event.sender.send('asynchronous-reply', 'pong');
  6. });
  7. ipcMain.on('synchronous-message', function(event, arg) {
  8. console.log(arg); // prints "ping"
  9. event.returnValue = 'pong';
  10. });
  11. ipcMain.once('once-message', function(event, arg) {
  12. console.log(arg); // prints "once"
  13. event.returnValue = 'once';
  14. });

渲染线程

  1. // In renderer process (web page).
  2. const ipcRenderer = require('electron').ipcRenderer;
  3. console.log(ipcRenderer.sendSync('synchronous-message', 'ping')); // prints "pong"
  4. ipcRenderer.on('asynchronous-reply', function(event, arg) {
  5. console.log(arg); // prints "pong"
  6. });
  7. ipcRenderer.send('asynchronous-message', 'ping');
  8. ipcRenderer.send('once-message', 'once');

ipcMain API:

  1. //监听
  2. ipcMain.on(channel, listener)
  3. ipcMain.once(channel, listener)
  4. //移除监听
  5. ipcMain.removeListener(channel, listener)
  6. ipcMain.removeAllListeners([channel])
  7. //event
  8. event.returnValue
  9. event.sender //sender 即 webContents

ipcRender API:

  1. //监听
  2. ipcRenderer.on(channel, listener)
  3. ipcRenderer.once(channel, listener)
  4. //移除
  5. ipcRenderer.removeListener(channel, listener)
  6. ipcRenderer.removeAllListeners([channel])
  7. //发送消息
  8. ipcRenderer.send(channel[, arg1][, arg2][, ...])
  9. ipcRenderer.sendSync(channel[, arg1][, arg2][, ...])
  10. ipcRenderer.sendToHost(channel[, arg1][, arg2][, ...])//不同渲染进程间的通信

注意

  • 主进程和渲染进程可以相互通信,渲染进程之间可以相互通信。
  • 事件分为同步和异步事件。
  • 事件的监听分为一次性和非一次性的。
  • 事件可移除。

文件操作

Electron 提供对原生文件对象进行了扩展,为 file 文件添加了 path 属性,方便开发者更灵活的处理文件操作。

比如文件的拖拽保存:

HTML中监听拖拽事件,获取文件后将文件路径传递给主线程,由主线程完成 copy 操作。

  1. const ipcRenderer = require('electron').ipcRenderer;
  2. function saveFile(fielPath,fileName){
  3. ipcRenderer.sendSync('synchronous-message', filePath,fileName);
  4. }
  5. document.addEventListener('drop', (e) => {
  6. e.preventDefault();
  7. e.stopPropagation();
  8. for (const f of e.dataTransfer.files) {
  9. console.log('File(s) you dragged here: ', f.path)
  10. saveFile(f.path,f.name);
  11. }
  12. });
  13. document.addEventListener('dragover', (e) => {
  14. e.preventDefault();
  15. e.stopPropagation();
  16. });

主线程监听 **save-file**事件,执行copy 操作。

  1. const ipcMain = require('electron').ipcMain;
  2. const fs = require('fs');
  3. const path = require('path');
  4. ipcMain.on('save-file', function(event, filePath,fileName) {
  5. fs.copyFileSync(filePath, path.join(__dirname,'file',fileName));
  6. });