Electron

1,158 阅读8分钟

参考electron文档: www.electronjs.org/docs

简介

electron是由Github开发,是一个用Html、css、JavaScript来构建桌面应用程序的开源库,可以打包为Mac、Windows、Linux系统下的应用。 image.png

核心

Electron 包含三个核心:

  • Chromium 为Ele ctron 提供强大的UI能力,可以在不考虑兼容性的情况下开界面

  • Node.js用于本地文件 系统和操作系 统

  • 系统API 为了提供原生系统的GUI支持,Electron内置了原生应用程序接口,对调用一些系统功能,如调用系统通知、打开系统文件夹提供支持。

可以理解为,electron是一个得到了Node.js和基于不同平台的Native APIs加强的Chromium浏览器,可以用来开发跨平台的桌面级应用

为什么选择electron

IMG20210430_103007.jpg

优点:

跨平台

  • 上手难度低。能够使用react、vue等前端框架,能方便地迁移前端组件,构建出漂亮的桌面应用。
  • 是目前最廉价的跨平台技术方案,HTML+JS 有大量的前端技术人员储备,而且有海量的现存web UI 库。
  • Electron由github主导,有成熟的产品atom和vs code,Intel和微软都有参与,社区更活跃。

缺点:

  • 应用体积过大。为了实现跨平台,它将整个V8引擎和Chromium内核都打包进去,在mac上至少是45M+起步,在windows上稍微好一点,不过也要35M+起步。不过相比早期打包体积100M+起步来说,已经好了不少。不过解压后安装依然是100M+起步。
  • 应用性能依旧是个问题。

有哪些著名应用是用Electron开发的

image.png

  • VSCode : 程序员最常用的开发者工具。 Atom : 是Github开发的文本编辑器。
  • slack : 聊天群组 + 大规模工具集成 + 文件整合 + 搜索的一个工具。就是把很多你常用的工具整合到了一起。
  • wordPress : 基于PHP开发的Blog搭建工具,新版本使用了Electron.

安装

cnpm install -g electron //全局安装 electron

npx electron -v // 版本查看

大体上,一个 Electron 应用的目录结构如下:

my-electron-app/
├── index.html
├── main.js
└── package.json
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Hello World!</title>
</head>
<body>
    <h1>Hello World!</h1>
</body>
</html>
const { app, BrowserWindow } = require('electron')

let mainWindow = null

app.on('ready', () => { // 监听准备事件
  mainWindow = new BrowserWindow({
    height: 500,
    width: 500,
    webPreferences: { //允许使用node的API
      nodeIntegration: true
    }
  })
  mainWindow.loadFile('index.html')
  mainWindow.on('closed', () => { // 监听关闭时间
    mainWindow = null // 监听窗口关闭时销毁,否则的话内存会一直被占用
  })
})
{
    "name": "electron-demo",
    "version": "0.0.1",
    "description": "electron-demo",
    "main": "main.js",
    "author": "sakurako",
    "scripts": {
        "start": "electron ."
      }
  }
  

Electron 应用程序使用package.json文件作为主入口(像任何其它的 Node.js 应用程序)。 应用程序的主脚本是main.js,所以相应修改package.json文件:

进程

image.png

image.png

Main主进程

主要通过Node.js、Chromium和Native APIs来实现一些系统以及底层的操作, 一些只能或适合在主进程做的事情。例如创建APP的窗口、创建系统级别的菜单、操作剪贴板、全局快捷键处理、托盘等。

app、BrowserWindow、ipcMain、Menu、Tray、dialog、Notification、webContents、globalShortcut、autoUpdater等。

Renderer渲染进程

主要通过Chromium来实现APP的图形界面——就是平时我们熟悉的前端开发的部分 一个主进程可以多个渲染进程 ipcRenderer、desktopCapture等。

Electron生命周期:

app的常用生命周期钩子:

  • will-finish-launching 在应用完成基本启动进程之后触发
  • ready 当electron完成初始化后触发;
  • window-all-closed 所有窗口都关闭的时候触发,在windows和linux里,所有窗口都退出的时候通常是应用退出的时候
  • before-quit 退出应用之前的时候触发
  • will-quit 所有窗口都已经关闭,即将退出应用的时候触发
  • quit 应用退出的时候触发
  • ...

而我们通常会在ready的时候执行创建应用窗口、创建应用菜单、创建应用快捷键等初始化操作。而在will-quit或者quit的时候执行一些清空操作,比如解绑应用快捷键。

跟app模块一样,BrowserWindow也有很多常用的事件钩子:

  • closed 当窗口被关闭的时候
  • focus 当窗口被激活的时候
  • show 当窗口展示的时候
  • hide 当窗口被隐藏的时候
  • maxmize 当窗口最大化时
  • minimize 当窗口最小化时
  • ...

针对不同的业务逻辑你需要对窗口进行不一样的操作。这个需要跟你的项目需求相匹配。比如上述说到的,windows的顶部的操作区(放大、缩小、关闭按钮)就可以通过icon模拟+实例方法来实现。

web-Contents生命周期事件 使用:mainWindow.webContents.on('事件名',回调);

配置:

  • 热加载 安装electron-reloader

npm install electron-reloader --save-dev

在主进程js文件(main.js/index.js)加入如下代码:

try {
        require('electron-reloader')(module);
} catch (_) { }
  • Electron 打开开发者工具 devtools 在主进程main.js加入如下代码:
mainWindow.webContents.openDevTools({mode:'right'});

主进程渲染进程之间的通讯

  • ipcMain 从主进程到渲染进程的异步通信。
  • ipcRenderer 从渲染器进程到主进程的异步通信。
  • BrowserWindow webContents结合主进程实现渲染进程和渲染进程通信

ipcMain & ipcRenderer

// 在主进程中.
const { ipcMain } = require('electron')
ipcMain.on('asynchronous-message', (event, arg) => {
  console.log(arg) // prints "ping"
  event.reply('asynchronous-reply', 'pong')
})

//在渲染器进程 (网页) 中。
const { ipcRenderer } = require('electron')
console.log(ipcRenderer.sendSync('synchronous-message', 'ping')) // prints "pong"

ipcRenderer.on('asynchronous-reply', (event, arg) => {
  console.log(arg) // prints "pong"
})
ipcRenderer.send('asynchronous-message', 'ping')

注意:ipcMain无法主动发消息给ipcRenderer。因为ipcMain只有.on()方法没有.send()的方法。所以只能用其他方法来实现,webContents。

webContents

webContents其实是BrowserWindow实例的一个属性。也就是如果我们需要在main进程里给某个窗口某个页面发送消息,则必须通过win.webContents.send()方法来发送。

代码大致如下:

// In main process
let win = new BrowserWindow({...})
win.webContents.send('img-files', imgs)


// In renderer process
ipcRenderer.on('img-files', (event, files) => {
  console.log(files)
})

所以必须指定要发送的窗口,才能将信息准确送达。

常用的一些api

通知Notification

从主进程中引入Notification类

  • title: 通知的标题,可以显示在通知栏上
  • option: 消息通知的各种属性配置,以对象的形式进行配置。
const { Notification } = require('electron')

function showNotification ()
  const notification = {
    title: '标题',
    body: '内容'
  }
  new Notification(notification).show()
}

app.whenReady().then(createWindow).then(showNotification)

托盘

当主进程监听关闭事件时,这时我们就可以开启托盘功能。我们需要使用Electron的两个模块Menu与Tray。

const { app, Menu, Tray } = require('electron')

let tray = null
app.whenReady().then(() => {
  tray = new Tray('/path/to/my/icon')
  const contextMenu = Menu.buildFromTemplate([
    { label: 'Item1', type: 'radio' },
    { label: 'Item2', type: 'radio' },
    { label: 'Item3', type: 'radio', checked: true },
    { label: 'Item4', type: 'radio' }
  ])
  tray.setToolTip('This is my application.')
  tray.setContextMenu(contextMenu)
})

托盘闪烁

let timer=setInterval(function() {
    count++;
    if (count%2 == 0) {
        appIcon.setImage(path.join(__dirname,'empty.ico'))
    } else {
        appIcon.setImage(path.join(__dirname,'lover.png'))
    }
}, 500);

弹出框 dialog

//下面是一个选择多个文件的对话框示例:
const { dialog } = require('electron') //渲染进程 const { dialog } = require('electron').remote
console.log(dialog.showOpenDialog({ properties: ['openFile', 'multiSelections'] }))

自定义软件顶部菜单、自定义右键菜单

创建菜单menu.js

主进程require

let template = [{
label: 'Edit',
submenu:[
        {
        label:'item1',
        click:function(){}
        },
        {
        type: 'separator'
        }
        ],
}];


const menu = Menu.buildFromTemplate(template) // 根据template生成一个Menu对象

Menu.setApplicationMenu(menu) // 根据menu生成菜单

剪切板

const { clipboard } = require('electron')

clipboard.writeText('Example String', 'selection')
console.log(clipboard.readText('selection'))
var app = require('app');
 
var globalShortcut = require('electron').globalShortcut;
app.on('ready', function() {
    // Register a 'ctrl+x' shortcut listener.
    var ret = globalShortcut.register('ctrl+x', function() {
        console.log('ctrl+x is pressed');
    })
    if (!ret) {
        console.log('registration failed');
    } // Check whether a shortcut is registered.
    console.log(globalShortcut.isRegistered('ctrl+x'));
});
app.on('will-quit', function() {
    // Unregister a shortcut.
    globalShortcut.unregister('ctrl+x');
    // Unregister all shortcuts.
    globalShortcut.unregisterAll();
});

离线上线监听

其实这个是JavaScript的一种方式进行监听网络状态,监听的事件分别是online和offline。

  • online : 如果链接上网络,就会触发该事件。
  • offline : 如果突然断网了,就会触发该事件。
const alertOnlineStatus = () => { window.alert(navigator.onLine ? 'online' : 'offline') }

window.addEventListener('online', alertOnlineStatus)
window.addEventListener('offline', alertOnlineStatus)

alertOnlineStatus()

electron 打包

  • electron-packager 打包完成的是可执行文件。
  • electron-builder 打包完成的是安装包。有更丰富的的功能,支持更多的平台,支持自动更新,打出的包更为轻量,并且可以打包出不暴露源码的setup安装程序。

npm install electron-builder --save-dev

npm install electron-builder -g
electron-builder --version

electron-builder

"build": {  
    "appId": "com.leon.HelloWorld02",//包名  
    "copyright":"LEON",//版权  
    "productName":"HelloWorld02",//项目名  
    "dmg":{  
      "background":"res/background.png",//背景图片路径  
      "window":{  
		//窗口左上角起始坐标  
		"x":100,
        "y":100,  
		//窗口大小  
        "width":500,  
        "height":300  
      }  
    },  
    "win": {  
        "icon": "res/icon.ico"//图标路径  
    }  
  }

打包可能遇到的报错: blog.csdn.net/q1059997113…

打包优化: 缩减包体积

electron应用的更新

electron应用的自动更新其实社区有很好的解决方案electron-updater。而electron-vue也在主进程的main/index.js里预先帮我们写好了一段注释的代码:

// import { autoUpdater } from 'electron-updater'

// autoUpdater.on('update-downloaded', () => {
//   autoUpdater.quitAndInstall()
// })

// app.on('ready', () => {
//   if (process.env.NODE_ENV === 'production') {
//     autoUpdater.checkForUpdates()
//   }
// }

electron-vue

  • 只有一个package.json。而大部分其他的项目结构依然在使用两个package.json来应对main进程和renderer进程的依赖库。
  • 内建完整的vue全家桶,省去再次配置vue-router和vuex的一些初期操作。
  • 内建完整的webpack开发、生产等配置,开发环境舒适。
  • 内建完整的开发、构建等npm scripts,使用非常方便。
  • 内建完整的Travis-ci、Appveyor配置脚本,只需少数修改就能做到利用CI自动构建的应用发布。
  • 完善的文档,清晰的项目结构

安装

# 如果你没有vue-cli的话需要全局安装
npm install -g vue-cli
# 然后使用vue-cli来安装electron-vue的模板
vue init simulatedgreg/electron-vue my-project

# 安装依赖
cd my-project
yarn # or npm install
# 进入开发模式
yarn run dev # or npm run dev

安装成功后

IMG20210421_173353.jpg

my-project
├─ .electron-vue  
│  └─ <build/development>.js files
├─ build
│  └─ icons/
├─ dist
│  ├─ electron/
│  └─ web/
├─ node_modules/
├─ src
│  ├─ main # 主进程
│  │  ├─ index.dev.js
│  │  └─ index.js
│  ├─ renderer # 渲染进程
│  │  ├─ components/
│  │  ├─ router/
│  │  ├─ store/
│  │  ├─ App.vue
│  │  └─ main.js
│  └─ index.ejs
├─ static/
├─ test
│  ├─ e2e
│  │  ├─ specs/
│  │  ├─ index.js
│  │  └─ utils.js
│  ├─ unit
│  │  ├─ specs/
│  │  ├─ index.js
│  │  └─ karma.config.js
│  └─ .eslintrc
├─ .babelrc
├─ .eslintignore
├─ .eslintrc.js
├─ .gitignore
├─ package.json
└─ README.md

传送门: electron-vue文档 electron-vue,作者为我们封装好了一个基于vue框架的脚手架,包括electron所有基本的开发构建工具 和vue配套的请求,路由以及vuex等插件。通过脚手架我们可以直接进入开发阶段,开发的同时,去了解electron的工作机制,之后再开始深入去理解她更深层次的代码逻辑。 先走形,再走心。

juejin.cn/post/691382…