vue3 + electron搭建一个简单的桌面端
一、初始化
-
技术选型
electron22、vite、vue3
由于electron23以及以后的版本将不会支持windows 7/8/8.1(来自),这里主要选取electron22
-
步骤
-
创建vite项目
// 创建项目 yarn create vite // 添加pina yarn add pinia
-
安装electron以及依赖
// 安装electron yarn add electron@latest // 安装nodemon,监听文件,热更新 yarn add nodemon // 安装electron-builder打包 yarn add electron-builder // 安装concurrently,同时执行多个命令,因为需要先启动vite再启动electron yarn add concurrently
-
安装element-plus
yarn add element-plus
-
配置目录结构
将页面代码与electron代码分别放在render以及electron-main文件夹下
electron-main文件夹中包括
- modules //模块文件夹
- controller //控制窗口通信
- preload // 预加载脚本
- shortcut // 快捷键配置
- tray // 托盘配置
- util // 工具类
- windows // 窗口配置
5. 配置package.json
// 启动模块
"scripts": {
"serve": "concurrently "yarn dev" "yarn start" ",
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview",
"start": "nodemon --exec electron . --watch ./ --ext .js,.html,.scss,.vue,.ts,.css",
"dist": "electron-builder"
},
// 打包模块
"build": {
"productName": "德为影像",
"appId": "com.dewei",
"copyright": "@dewei",
"asar": true,
"directories": {
"output": "build"
},
"files": [
"./dist",
"./package.json",
"./src/electron-main"
],
"nsis": {
"oneClick": false,
"allowElevation": true,
"allowToChangeInstallationDirectory": true,
"createDesktopShortcut": true,
"createStartMenuShortcut": true
},
"dmg": {
"contents": [
{
"x": 410,
"y": 150,
"type": "link",
"path": "/Applications"
},
{
"x": 130,
"y": 150,
"type": "file"
}
]
},
"mac": {
"icon": "build/001.png"
},
"win": {
"icon": "build/001.png",
"target": [
{
"target": "nsis",
"arch": [
"ia32"
]
}
]
},
"linux": {
"icon": "build/001.png"
},
"extraResources": [
{
"from": "./public/PrintScr.exe",
"to": "./extraResources/PrintScr.exe"
},
{
"from": "./public/PrScrn.dll",
"to": "./extraResources/PrScrn.dll"
}
]
},
// 需要配置项目的主入口,以及项目信息
"name": "mi-desktop",
"private": true,
"version": "0.0.0",
"main": "src/electron-main/main.js",
"author": "dewei",
"description": "ceshi",
二、进程之间的通信
-
IPC通道
electron使用ipcMain和ipcRenderer模块来定义通道经常数据传输
通常在controller文件夹中编写各个窗口的主进程代码,在perload文件夹中编写预加载脚本。electron会在全局的window下暴露出electronAPI,在渲染器中可以使用window.electronAPI.functionName来使用预加载脚本中暴露的方法
-
渲染器到主进程(单项)
在主进程中通过ipcMain.on监听事件。注意:事件名称唯一
ipcMain.on('event-name', (e, value) => { console.log(value) });
在预加载脚本中
const { contextBridge, ipcRenderer } = require('electron'); const sendName = (value) => { ipcRenderer.send('event-name', value); }; contextBridge.exposeInMainWorld('electronAPI', { sendName });
在渲染器也就是vue文件中
const handleSendName = async () => { window.electronAPI.sendName('这是名字'); };
-
渲染器到注进程(双向)
在主进程中监听事件
ipcMain.handle('get-url', (e,value) => { let url = '' if(value=='bbb'){ url = 'aaaa' } return url; });
在预加载中暴露方法
const { contextBridge, ipcRenderer } = require('electron'); const getUrl = async (value) => { const url = await ipcRenderer.invoke('get-url', value); return url; }; contextBridge.exposeInMainWorld('electronAPI', { getUrl });
在渲染器中使用方法
const urlSring = async () => { let url = await window.electronAPI.getUrl('bbb'); };
-
主进程到渲染器
在主进程中发送
mainWindow.webContents.send('send-info', info);
预加载暴露方法
const { contextBridge, ipcRenderer } = require('electron'); const getInfo = (info) => ipcRenderer.on('send-info', info); contextBridge.exposeInMainWorld('electronAPI', { getInfo });
渲染器中监听
window.electronAPI.getInfo((_event, value) => { console.log(value) })
三、简单的通知功能
在渲染器中将接口获取到的数据传送到主进程中,在主进程中调用电脑的消息通知模块
渲染器
window.electronAPI.openNotification(msg);
预加载
const openNotification = async (message) => {
let result = await ipcRenderer.invoke('on-openNotification-event', message);
};
主进程
const { ipcMain, Notification } = require('electron');
// 监听主窗口发送通知事件
const openNotification = () => {
ipcMain.handle('on-openNotification-event', (event, message) => {
const NOTIFICATION_TITLE = '您有一条新消息';
const NOTIFICATION_BODY = message;
new Notification({
title: NOTIFICATION_TITLE,
body: NOTIFICATION_BODY,
silent: true,
icon: '../../tray/icon.ico',
timeoutType: 'never',
}).show();
});
};
四、截屏功能
新建一个截屏窗口,这个窗口为全屏透明,没有边框
const { LOAD_URL } = require('./config.js');
const path = require('path');
const isDev = require('electron-is-dev');
const { screen } = require('electron');
const mainWinURL = isDev ? `http://localhost:8080/#/cut` : `${LOAD_URL}#/cut`;
const getSize = () => {
const { size, scaleFactor } = screen.getPrimaryDisplay();
return {
width: size.width * scaleFactor,
height: size.height * scaleFactor,
};
};
const createCutWindow = (BrowserWindow) => {
const { width, height } = getSize();
const win = new BrowserWindow({
width,
height,
autoHideMenuBar: true,
useContentSize: true,
movable: false,
frame: false,
resizable: false,
hasShadow: false,
transparent: true,
fullscreen: true,
simpleFullscreen: true,
alwaysOnTop: false,
// opacity: 0.3,
show: false,
webPreferences: {
webSecurity: true,
nodeIntegration: true,
contextIsolation: true,
// 渲染器进程到主进程通信
preload: path.resolve(__dirname, '../modules/preload/cut.js'),
},
});
// 加载页面地址 线上内网可切换地址
win.loadURL(mainWinURL);
// 开发者工具
win.webContents.openDevTools();
win.maximize();
win.setFullScreen(true);
// 优雅打开界面
// win.once('ready-to-show', () => {
// win.show();
// });
global.cutWindow = win;
};
module.exports = {
createCutWindow,
};
然后再窗口初始化完成的时候在主进程中获取屏幕信息,将屏幕信息转化为url然后显示在渲染器上
const { id } = screen.getPrimaryDisplay();
let url = '';
let sources = await desktopCapturer.getSources({
types: ['screen'],
thumbnailSize: getSize(),
});
for (let source of sources) {
if (parseInt(source.display_id, 10) === id) {
url = source.thumbnail.toDataURL('image/png');
}
}
页面代码
let bg = ref('');
在页面中引入canvas绘图工具konva。通过canvas选取要截图的信息
五、截屏结合百度ocr
在上一步获取到截屏信息之后,将图片信息转化为base64格式并存储到pinia中,调取百度的ocr功能。
在百度AI开放平台中申请建好项目,然后根据key获取token,详细操作见官网
通过watch监听pinia中的截图信息,当截图后调用文字识别接口
// 文字识别
const startOcr = () => {
const base64Img = cutImage.value;
const img = base64Img.replace(/^data:image/w+;base64,/, ''); //去掉base64位头部
const param = {
image: img,
};
axios({
method: 'post',
baseURL: PATH_URL + '/rest/2.0/ocr/v1/accurate_basic?access_token=' + accessToken,
data: param,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
})
.then((res) => {
wordsList.value = res.data.words_result;
})
.catch((err) => {
console.error(err);
});
};
六、开机自启动
在主进程中写入下列代码即可
const onAppReady = () => {
// 设置开机自启
const exeName = path.basename(process.execPath);
app.setLoginItemSettings({
// 设置为true注册开机自启
openAtLogin: true,
openAsHidden: false, //macOs
path: process.execPath,
args: ['--processStart', `"${exeName}"`],
});
};
app.isReady() ? onAppReady() : app.on('ready', onAppReady);