同步更新 微信公众号【爱写代码的小任】 欢迎关注
背景
Electron是一个强大的框架,可以帮助开发者使用Web技术(HTML、CSS和JavaScript)构建跨平台的桌面应用程序。它已经被广泛应用于许多知名应用的开发,如Visual Studio Code、Slack和GitHub Desktop等。
在最近的工作中, 需要开发使用类似的这样一个桌面应用程序,于是就和代码搭子一起合作, 带您一步步了解如何使用Electron开发您的第一个应用。
理论知识
1. 架构
Electron应用由两个主要进程组成:主进程和渲染进程。主进程负责管理应用的生命周期、创建和控制渲染进程以及与底层操作系统进行交互。渲染进程是基于Chromium的浏览器进程,负责呈现和处理Web页面。
2. 主进程
主进程是Electron应用的入口点,通常在main.js文件中定义。它是一个Node.js进程,可以使用Electron的API调用底层操作系统的功能,如创建窗口、处理系统事件和执行文件操作等。
3. 渲染进程
染进程是在Electron窗口中加载的Web页面运行的进程。每个窗口都有一个单独的渲染进程,使用Chromium提供的Web技术来呈现用户界面和处理用户交互。渲染进程使用Electron的API与主进程进行通信,实现进程间的消息传递和协作。
4. 主进程和渲染进程的通信
Electron提供了IPC(进程间通信)机制,允许主进程和渲染进程之间进行通信。使用Electron的ipcMain和ipcRenderer模块,开发者可以在主进程和渲染进程之间发送和接收消息,以实现进程间的数据传递和事件触发。
5. 打包和分发
Electron应用可以使用Electron Builder、electron-packager、Electron Forge等工具进行打包和分发。这些工具可以将应用程序打包为可执行文件或安装程序,以便在不同操作系统上进行部署和分发。
6. 应用扩展
过使用Electron的模块系统,开发者可以使用众多的第三方模块和插件来扩展应用的功能。这些模块可以用于实现文件系统操作、数据库访问、网络请求、GUI组件等,为应用提供更多的能力和灵活性。
7. 安全性
由于Electron应用运行在本地环境中,开发者需要注意安全性方面的考虑。例如,需要注意处理用户输入的安全性、防范跨站脚本攻击(XSS)、限制访问本地资源等。
实践过程
1. 创建项目
在开始之前,确保您已经安装了Node.js和npm(Node.js的包管理器)
因为Electron下载默认从国外,一般会有限制,建议提前执行如下配置,获取最好的下载效果
# pnpm
pnpm config set electron_mirror "https://registry.npmmirror.com/-/binary/electron/"
# yarn
yarn config set electron_mirror "https://registry.npmmirror.com/-/binary/electron/"
准备主进程代码 main.js
// 省略其他代码
const mainWindow = new BrowserWindow({
frame: true,//创建无边框窗口
resizable: false, //不允许用户改变窗口大小
width: 800, //设置窗口宽高
height: 600,
icon: iconPath, //应用运行时的标题栏图标
webPreferences: {
backgroundThrottling: false, //设置应用在后台正常运行
nodeIntegration: true, //设置能在页面使用nodejs的API
contextIsolation: false,
// preload: path.join(__dirname, './preload.js')
}
})
mainWindow.loadFile('index.html')
准备渲染进程代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
<link rel="stylesheet" href="css/base.css">
</head>
<body>
<h1>登录</h1>
<form action="/login" method="post">
<label for="username">用户名:</label>
<input type="text" id="username" name="username">
<br>
<label for="password">密码:</label>
<input type="password" id="password" name="password">
<br>
<img src="xx" @click="getCode" class="login-code-img" />
<input type="submit" value="登录">
</form>
</body>
<script src=" https://cdn.bootcdn.net/ajax/libs/axios/0.26.1/axios.min.js"></script>
<script src="js/login.js"></script>
</html>
准备渲染进程 JavaScript代码 login.js
const { ipcRenderer } = require('electron')
window.addEventListener('load', function () {
let login_img = document.querySelector('.login-code-img')
axios.defaults.baseURL = 'http://localhost:8080/api'
axios.get('/captchaImage').then(res => {
let codeUrl = "data:image/gif;base64," + res.data.img
login_img.src = codeUrl
})
//监听
ipcRenderer.on('mainsend', (event, arg) => {
console.log('渲染进程收到的消息:', arg)
})
// 表单验证
const form = document.querySelector("form");
form.addEventListener("submit", (event) => {
event.preventDefault();
ipcRenderer.send('setNewData', '渲染进程的数据')
// 获取表单数据
const username = document.querySelector("#username").value;
const password = document.querySelector("#password").value;
// 验证用户名和密码
if (username === "" || password === "") {
alert("用户名或密码不能为空");
return;
}
// 发送登录请求
const request = new XMLHttpRequest();
request.open("POST", "/login");
request.setRequestHeader("Content-Type", "application/json");
request.send(JSON.stringify({ username, password }));
// 处理登录结果
request.addEventListener("load", () => {
const response = JSON.parse(request.responseText);
if (response.success) {
// 登录成功,跳转到主页
window.location.href = "/";
} else {
// 登录失败,显示错误信息
alert(response.error);
}
});
});
})
准备 package.json
{
"name": "tasky",
"version": "1.0.0",
"description": "This is a task management app",
"main": "main.js",
"scripts": {
"dev": "electron .",
"make": "electron-forge make"
},
"dependencies": {
"react": "18.2.0",
"vue": "3.4.15"
},
"devDependencies": {
"cross-env": "7.0.3",
"electron": "28.2.0",
"nodemon": "^3.0.3"
}
}
2. 启动项目
$ pnpm dev
过一会儿,就会启动起来
3. 项目打包
Electron Forge 是一个用于加速Electron应用开发的工具集。它提供了一组命令行工具和脚手架,用于初始化、构建、打包和发布Electron应用。
# 自动导入 electron forge 相关依赖和文件
$ npx electron-forge import
这个时候,会在项目根目录生成 forge.config.js
module.exports = {
packagerConfig: {
asar: true,
},
rebuildConfig: {},
makers: [
{
name: '@electron-forge/maker-squirrel',
config: {},
},
{
name: '@electron-forge/maker-zip',
platforms: ['darwin'],
},
{
name: '@electron-forge/maker-deb',
config: {},
},
{
name: '@electron-forge/maker-rpm',
config: {},
},
],
plugins: [
{
name: '@electron-forge/plugin-auto-unpack-natives',
config: {},
},
],
};
同时自动配置项目依赖 和 运行命令
scripts: {
"make": "electron-forge make"
}
"devDependencies": {
"@electron-forge/cli": "^7.2.0",
"@electron-forge/maker-deb": "^7.2.0",
"@electron-forge/maker-rpm": "^7.2.0",
"@electron-forge/maker-squirrel": "^7.2.0",
"@electron-forge/maker-zip": "^7.2.0",
"@electron-forge/plugin-auto-unpack-natives": "^7.2.0",
}
执行 make 命令
$ pnpm run make
生成如下目录和包 Mac下如图
扩展知识
1. 进程间通信
渲染进程发送与接收
const { ipcRenderer } = require('electron')
window.addEventListener('load',function(){
// 渲染进程 监听主进程消息
ipcRenderer.on('mainsend', (event, arg) => {
console.log('渲染进程收到的消息:',arg)
})
// 表单验证
const form = document.querySelector("form");
form.addEventListener("submit", (event) => {
event.preventDefault();
// 渲染进程发送消息到主进程
ipcRenderer.send('setNewData', '渲染进程的数据')
// 获取表单数据
const username = document.querySelector("#username").value;
const password = document.querySelector("#password").value;
// 省略其他代码
});
})
主进程发送与接收
const electron = require('electron')
// 这里主要引入 ipcMain
const { app, BrowserWindow, ipcMain, Tray, Menu, screen, dialog } = electron
app.on('ready', () => {
// 注意: 这里要能确保ipc通信在生命周期里面
mainWindow.webContents.on('did-finish-load', () => {
// 主进程 发送
mainWindow.webContents.send('mainsend', '这是主进程的主动搭讪')
// 接收渲染进程的事件消息
ipcMain.on('setNewData', (event, data) => {
console.log('主进程接收---------------------111', data);
})
})
// 省略其他代码
})
2. 应用自动升级
准备测试升级文件
provider: generic,
url: "http://192.168.1.155:3000/checkUpdate"
客户端代码如下
/*
* 自动升级方法
*/
function checkUpdate() {
// 本机开发调试,需要指定如下配置
Object.defineProperty(app, 'isPackaged', {
get() {
return true;
}
})
// 本机开发调试,需要指定如下配置
autoUpdater.updateConfigPath = path.join(__dirname, 'dev-app-update.yml')
if (process.platform == 'darwin') {
autoUpdater.setFeedURL('http://192.168.1.155:3000/checkUpdate')
} else {
autoUpdater.setFeedURL('http://192.168.1.155:3000/checkUpdate')
}
autoUpdater.checkForUpdates()
autoUpdater.on('error', (err) => {
console.log('err', err)
})
autoUpdater.on('update-available', () => {
console.log('found new version')
})
autoUpdater.on('update-not-available', () => {
console.log('Notfound new version')
})
autoUpdater.on('update-downloaded', () => {
dialog.showMessageBox({
type: 'info',
title: '应用更新',
message: '发现新版本,是否更新?',
buttons: ['是', '否']
}).then((buttonIndex) => {
if (buttonIndex.response == 0) {
autoUpdater.quitAndInstall()
app.quit()
}
})
})
}
后面再应用 ready的时候调用即可
app.on('ready', () => {
//检查更新
checkUpdate()
// 省略其他代码
})
生成签名代码【主要是为了计算文件签名】
const crypto = require('crypto');
const fs = require('fs');
// 异步读取文件并计算SHA-512哈希
function getSha512Hash(filePath) {
return new Promise((resolve, reject) => {
fs.readFile(filePath, (err, data) => {
if (err) {
reject(err);
} else {
const hash = crypto.createHash('sha512');
hash.update(data);
resolve(hash.digest('hex'));
}
});
});
}
// 使用方法
const filePath = '/Users/i-sinosoft/Downloads/tasky-darwin-arm64-1.0.0.zip'; // 替换为你的文件路径
getSha512Hash(filePath).then(hash => {
console.log('SHA-512 Hash:', hash);
}).catch(error => {
console.error('Error generating SHA-512 hash:', error);
});
服务端升级API代码(测试)
const express = require("express")
const app = express()
app.get('/checkUpdate/:platform', (req, res, next) => {
let {platform} = req.params
let resinfo = null
// 返回 Windows 配置
if (platform == 'latest.yml') {
resinfo = {
name: 'v1.0.2',
version: 'v1.0.2',
path: "https://niaoshuai-book.oss-cn-beijing.aliyuncs.com/tasky-1.0.0%20Setup.exe",
notes: "xxxxxx",
sha512: '4d1a4a9f797a43e968483ee7dcc74ed25d1b0d256e979e68d310dce4cd131541ad0e84f0e4ddf23a424133f2736038c79c43aa3b45e9a280d487fbc693dd3f05'
}
}
// 返回 Mac 配置
if (platform == 'latest-mac.yml') {
resinfo = {
name: 'v1.0.3',
version: 'v1.0.3',
path: "https://niaoshuai-book.oss-cn-beijing.aliyuncs.com/tasky-darwin-arm64-1.0.0.zip",
notes: "xxxxxx", sha512:'8f7c5951a4a100eec0c05caa19d9a5e2392122830360301fd4ca2baae2dedac54ff6065d6aed03e433287863a5c0a250aa9a8bbdd5eba1b2756ec14e2ab4c01f'
}
}
if (!resinfo) {
resinfo = {code: 400, msg: '参数错误'}
}
res.send(resinfo)
})
app.listen(3000, () => {
console.log("express web server is listen at 3000 port!");
})
Mac下效果
Windows下效果
总结
通过本文, 您学会了如何使用Electron 开发第一个应用。您了解了创建主进程和渲染进程的基本步骤,并启动了一个简单的Electron应用;同时也使用Electron Forge可以减少开发者在配置和构建Electron应用程序时的工作量,并提供一致的开发和分发体验。
最后希望这个经验能够激发您的创造力,探索Electron的更多功能,并构建出令人惊艳的跨平台桌面应用。
代码地址: https://gitee.com/eight-liang-listener/electron_first.git
关注我,实时获取我的更新动态,和我一起讨论