基于技术

  • Chromium
  • Node.js

运行原理

Chromium 提供了 UI 能力
Electron 内置Node.js ,提供了操作系统底层API的能力,常用模块 Path、fs、Crypto
内置原生应用程序接口调用

QT 对比

跨平台应用开发,QT对比

项目初始化

1
2
3
npm install @vue/cli -g
vue create electron-vue
vue add electron-builder

安装卡顿 node install.js

1
2
# ~/.npmrc 单独设置 Electron 的镜像
electron_mirror="https://npm.taobao.org/mirrors/electron/"

启动程序

npm run electron:serve

failed to install correctly

  1. https://npm.taobao.org/mirrors/electron/11.2.3/ 下载 electron-v11.2.3-win32-x64.zip
  2. node_modules\electron\下创建dist文件夹
  3. 下载的压缩包解压进刚刚创建的dist文件夹
  4. node_modules\electron\中创建path.txt,内容为electron.exe

进程、常用功能

  1. 主进程
    处理原生应用逻辑,运行package.json的main脚本的进程被称为主进程,有且仅有一个主进程
    创建窗口等所有系统事件都在主进程中进行
  • 设置渲染进程的大小、外观
1
2
3
4
5
6
7
8
win = new BrowserWindow({
width: 1200,
height: 600,
webPreferences: {
webSecurity: false, // 是否禁用浏览器跨域安全特性
nodeIntegration: true // 是否完整支持 node
}
})
  • 设置菜单
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function createMenu(){
// darwin 表示 macOS
if(process.platform === 'darwin'){
const template = [
label: 'App',
submenu: [
{
role: 'about'
},
{
role: 'quit'
}
]
]
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
} else {
Menu.setApplicationMenu(null) // windows、linux 系统
}
}
  • 当应用启动后,初始化完成需要做的事情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
app.on('ready', async() => {
if(isDevelopment && !process.env.IS_TEST){
try{
// 开发环境安装 Vue DevTools
await installVueDevtools()
} catch(e){
// vue devtools 安装失败
}
}
// 全局注册快捷键
globalShortcut.register('CommandOrControl+Shift+i', function(){
win.webContents.openDevTools()
})
// 创建渲染进程
createWindow()
})
  • 当所有窗口关闭时要做的事情
1
2
3
4
5
app.on('window-all-closed', () => {
if(process.platform !== 'darwin'){
app.quit()
}
})
  • 与渲染进程进行通讯

ipcRenderer(渲染进程中使用的对象)
ipcMain(主进程中使用的对象)

  • 打开新窗口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const winURL =
process.env.NODE_ENV === 'development'
? 'http://localhost:8080'
: `file://${__dirname}/index.html`

// 打开新窗口
let calendarWin

function openCalendarWindow () {
calendarWin = new BrowserWindow({
width: 400,
height: 550,
parent: win, // win是主窗口
webPreferences: {
nodeIntegration: true
}
})
calendarWin.loadURL(winURL + '#/Calendar')
calendarWin.on('closed', () => {
calendarWin = null
})
}

ipcMain.on('openCalendarWindow', e => openCalendarWindow())
  1. 渲染进程
    负责界面渲染
    每创建一个页面都会创建一个渲染进程
    每个页面运行在自己的渲染程序中
    每个渲染进程都是独立的,只关心自己所运行的页面

  2. 系统通知
    通知一共两种调用方式:

    1. HTML5 Notification API 渲染进程调用
    2. Notification 模块 主进程调用
  3. 设置托盘
    托盘属于系统级操作,主进程中设置,且必须在程序 ready 后设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 设置托盘
tray = new Tray('./src/assets/icon_tray.png')
// 设置托盘菜单
const trayContextMenu = Menu.buildFromTemplate([
{
label: '打开',
click: () => {
win.show()
}
},
{
label: '退出',
click: () => {
app.quit()
}
}
])
tray.setToolTip('electron')
tray.on('click', () => {
win.show()
})
tray.on('right-click', () => {
tray.popUpContextMenu(trayContextMenu)
})

打包

  • 设置 appid

    package.json (”appId”: “com.ipp.electronvue”)

  • vue.config.js 配置 electron-builder 参数

调用 C++ Dll

1
2
3
4
5
1. npm install node-gyp -g
2. npm install windows-build-tools -g

npm install ffi-napi --save
npm install electron-edge-js --save

注意:python 版本必须 2.7

ffi-napi

1
2
3
4
5
6
7
const ffi = require('ffi-napi')
const path = require('path')
const Dll = ffi.Library(path.resolve('resources/dll/MyDLL.dll'), {
Add: ['float', ['float', 'float']],
Hello: ['string', []],
StrLength: ['int', ['string']]
})

electron-edge-js

windows专属功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
edge = require('electron-edge-js')
invoke = edge.func({
assemblyFile: path.resolve('resources/dll/electronedge.dll'),
typeName: 'electronedge.Class1',
methodName: 'Invoke'
})

if (process.platform === 'win32') {
invoke('这是自定义字符串', function (err, val) {
if (err) throw err
console.log(val)
this.$message({
message: 'dll返回的内容为:' + val,
type: 'success'
})
})
} else {
this.$notify.error({
title: '错误',
message: '此功能为windows专属功能,mac无法使用'
})
}

报错解决方案:vue.config.js 配置 externals: ['electron-edge-js']

集成 sqlite3

需要安装node-gyp和windows-build-tools

npm install sqlite3 --save
npm install (否则会出现找不到sqlite3.node)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import sq3 from 'sqlite3'
const pathUtil = require('../utils/pathUtil.js')

export const dbPath = pathUtil.getAppResourcePath('db/data.sqlite1')

const sqlite3 = sq3.verbose()
const db = new sqlite3.Database(dbPath)

// 初始化
db.serialize(() => {
db.run('create table test(name varchar(15))', function () {
db.run("insert into test values('hello,word')", function () {
db.all('select * from test', function (err, res) {
if (!err) {
console.log(JSON.stringify(res))
} else {
console.log(err)
}
})
})
})
})
export default db

执行 CMD

  • 引用 child_process 模块
1
import { exec } from ‘child_process’
1
2
// 解决中文乱码问题
npm install iconv-lite --save

electron 更新

1
npm install electron-updater --save-dev

扩展

跨浏览器消息通讯

postMessage 可以实现不同源的页面在iframe中通讯, 但是不能跨浏览器窗体。
localStorage 存储的值发生变化时会在当前窗体window触发 storage 事件。 多个同源的页面是共享localStorage,因此浏览器同时打开多个同源页面,只要localStorage改变了,都会收到 storage 事件。

消息传递过程

学习资料