自动更新概述
Electron 应用使用 electron-updater 库实现自动更新,它支持:
- 增量更新(下载差量包)
- 静默下载,后台更新
- 用户确认后再安装
- 更新回滚
核心概念
- Publisher:发布更新包的服务器
- Update Server:托管更新包的服务器(GitHub、AWS S3、Nexus 等)
- Client:应用内置的更新检查和下载逻辑
安装 electron-updater
npm install electron-updater
基本配置
const { autoUpdater } = require('electron-updater');
const { ipcMain } = require('electron');
// 配置日志
autoUpdater.logger = require('electron-log');
// 开发环境禁用自动更新
if (process.env.NODE_ENV === 'development') {
autoUpdater.autoUpdate = false;
}
更新服务器配置
GitHub Releases
最常用的更新服务器,免费且配置简单:
const { autoUpdater } = require('electron-updater');
autoUpdater.configure({
provider: 'github',
owner: 'your-username',
repo: 'your-repo'
});
通用服务器(S3、Nexus、自建)
autoUpdater.configure({
provider: 'generic',
url: 'https://update.example.com/' // 更新包托管地址
});
配合 electron-builder 使用
在 package.json 中配置:
{
"build": {
"publish": [
{
"provider": "github",
"owner": "your-username",
"repo": "your-repo"
}
]
}
}
更新流程
1. 检查更新
// main.js
const { autoUpdater } = require('electron-updater');
function checkForUpdates() {
// 返回一个 Promise
return autoUpdater.checkForUpdates();
}
// 检查并下载
function checkAndDownload() {
return autoUpdater.checkForUpdates()
.then(result => {
console.log('Update info:', result);
return result;
})
.catch(err => {
console.error('Update check failed:', err);
});
}
2. 监听更新事件
// 监听检查更新事件
autoUpdater.on('checking-for-update', () => {
console.log('Checking for updates...');
mainWindow.webContents.send('update:checking');
});
// 监听有可用更新
autoUpdater.on('update-available', (info) => {
console.log('Update available:', info.version);
mainWindow.webContents.send('update:available', info);
});
// 监听没有可用更新
autoUpdater.on('update-not-available', (info) => {
console.log('Update not available. Current version is latest.');
mainWindow.webContents.send('update:not-available', info);
});
// 监听下载进度
autoUpdater.on('download-progress', (progress) => {
const percent = Math.round(progress.percent);
console.log(`Downloaded ${percent}%`);
mainWindow.webContents.send('update:progress', {
percent,
bytesPerSecond: progress.bytesPerSecond,
transferred: progress.transferred,
total: progress.total
});
});
// 监听下载完成
autoUpdater.on('update-downloaded', (info) => {
console.log('Update downloaded:', info.version);
mainWindow.webContents.send('update:downloaded', info);
});
// 监听错误
autoUpdater.on('error', (err) => {
console.error('Update error:', err);
mainWindow.webContents.send('update:error', err.message);
});
3. 下载并安装更新
// 下载更新
async function downloadUpdate() {
try {
const result = await autoUpdater.downloadUpdate();
return result;
} catch (err) {
console.error('Download failed:', err);
throw err;
}
}
// 安装更新并重启
function installUpdate() {
autoUpdater.quitAndInstall();
}
渲染进程更新 UI
IPC 通信
// main.js - 处理渲染进程的更新请求
ipcMain.handle('update:check', async () => {
try {
const result = await autoUpdater.checkForUpdates();
return { success: true, info: result?.updateInfo };
} catch (err) {
return { success: false, error: err.message };
}
});
ipcMain.handle('update:download', async () => {
try {
await autoUpdater.downloadUpdate();
return { success: true };
} catch (err) {
return { success: false, error: err.message };
}
});
ipcMain.handle('update:install', () => {
autoUpdater.quitAndInstall();
});
Preload API
// preload.js
contextBridge.exposeInMainWorld('electronAPI', {
checkForUpdates: () => ipcRenderer.invoke('update:check'),
downloadUpdate: () => ipcRenderer.invoke('update:download'),
installUpdate: () => ipcRenderer.invoke('update:install'),
// 监听更新事件
onUpdateAvailable: (callback) => {
ipcRenderer.on('update:available', (event, info) => callback(info));
},
onUpdateProgress: (callback) => {
ipcRenderer.on('update:progress', (event, progress) => callback(progress));
},
onUpdateDownloaded: (callback) => {
ipcRenderer.on('update:downloaded', (event, info) => callback(info));
}
});
React 组件示例
// UpdateButton.jsx
import React, { useState, useEffect } from 'react';
function UpdateButton() {
const [status, setStatus] = useState('idle'); // idle, checking, available, downloading, ready
const [progress, setProgress] = useState(0);
const [updateInfo, setUpdateInfo] = useState(null);
useEffect(() => {
const api = window.electronAPI;
api.onUpdateAvailable((info) => {
setStatus('available');
setUpdateInfo(info);
});
api.onUpdateProgress((progress) => {
setStatus('downloading');
setProgress(progress.percent);
});
api.onUpdateDownloaded((info) => {
setStatus('ready');
});
}, []);
const handleCheck = async () => {
setStatus('checking');
await window.electronAPI.checkForUpdates();
};
const handleDownload = async () => {
await window.electronAPI.downloadUpdate();
};
const handleInstall = () => {
window.electronAPI.installUpdate();
};
return (
<div>
{status === 'idle' && <button onClick={handleCheck}>检查更新</button>}
{status === 'checking' && <span>检查中...</span>}
{status === 'available' && (
<div>
<span>发现新版本 {updateInfo?.version}</span>
<button onClick={handleDownload}>下载更新</button>
</div>
)}
{status === 'downloading' && (
<div>
<span>下载中 {progress}%</span>
<progress value={progress} max="100" />
</div>
)}
{status === 'ready' && (
<div>
<span>下载完成</span>
<button onClick={handleInstall}>安装并重启</button>
</div>
)}
</div>
);
}
签名与安全
macOS 代码签名
// electron-builder 配置
{
"build": {
"mac": {
"identity": "Developer ID Application: Your Name (TEAM_ID)",
"hardened-runtime": true,
"entitlements": "entitlements.plist",
"gatekeeper-assess": false
}
}
}
Windows 代码签名
{
"build": {
"win": {
"certificateFile": "./certificate.pfx",
"certificatePassword": "your-password"
}
}
}
更新包签名验证
electron-updater 会自动验证更新包的签名:
const { autoUpdater } = require('electron-updater');
// 确保签名验证
autoUpdater.autoDownload = false; // 先下载,验证后再安装
高级配置
差量更新
只下载变化的部分,减少更新包大小:
autoUpdater.fullChangelog = false; // 使用差量更新
限制更新范围
// 只检查 beta 版本
autoUpdater.allowPrerelease = true;
// 或者指定版本范围
autoUpdater.versionInfoToReturn = {
version: '2.0.0-beta.1',
releaseDate: new Date(),
releaseNotes: 'Beta release'
};
自定义更新检查
// 使用私有 API 进行自定义检查
async function customUpdateCheck() {
const response = await fetch('https://api.example.com/latest-version');
const data = await response.json();
const currentVersion = app.getVersion();
if (compareVersions(data.version, currentVersion) > 0) {
// 有新版本,手动设置更新信息
autoUpdater.updateInfo = {
version: data.version,
releaseDate: data.releaseDate,
releaseNotes: data.releaseNotes,
downloadUrl: data.downloadUrl
};
}
}
调试更新功能
开发环境禁用自动更新
if (process.env.NODE_ENV === 'development' || !app.isPackaged) {
autoUpdater.autoDownload = false;
autoUpdater.autoInstallOnAppQuit = false;
}
强制更新
// 检查更新前先清除缓存
autoUpdater.cache = null;
模拟更新流程
// 在开发环境中模拟更新事件
if (process.env.NODE_ENV === 'development') {
setTimeout(() => {
mainWindow.webContents.send('update:available', {
version: '2.0.0',
releaseNotes: 'New features'
});
}, 3000);
}
常见问题与解决方案
1. 更新检查失败
- 检查网络连接
- 确认 GitHub Releases 格式正确
- 查看 electron-updater 日志
2. 下载后无法安装
- Windows:确保应用有写入权限
- macOS:确保代码签名正确
3. 版本号格式
推荐使用 semver 格式:1.0.0、2.1.0-beta.1
4. 更新回滚
electron-updater 不直接支持回滚,可以通过以下方式实现:
// 在安装前备份当前版本
function backupCurrentVersion() {
const currentExe = process.execPath;
const backupPath = path.join(app.getPath('userData'), 'previous-version.exe');
fs.copyFileSync(currentExe, backupPath);
}
这一章想说的
Electron 自动更新是一个完整的系统工程:
- 更新服务器:GitHub Releases 是最简单选择
- 更新流程:检查 → 下载 → 安装
- 用户界面:通过 IPC 让渲染进程控制更新
- 安全:代码签名和包验证
- 调试:开发环境需要特殊处理
完善的自动更新机制能提升用户体验,确保用户始终使用最新版本。