自动更新是桌面应用的重要功能,可以让用户获得最新的功能和修复。Electron 提供了多种自动更新方案,本文将详细介绍如何实现自动更新。
electron-updater 基础
electron-updater 是最常用的 Electron 自动更新解决方案。
安装
bashnpm install electron-updater
基本配置
javascript// main.js const { app, BrowserWindow } = require('electron') const { autoUpdater } = require('electron-updater') let mainWindow app.whenReady().then(() => { createWindow() // 检查更新 autoUpdater.checkForUpdatesAndNotify() }) function createWindow() { mainWindow = new BrowserWindow({ width: 800, height: 600 }) mainWindow.loadFile('index.html') }
配置更新服务器
1. GitHub Releases (推荐)
json// package.json { "build": { "publish": { "provider": "github", "owner": "your-username", "repo": "your-repo" } } }
javascript// main.js autoUpdater.setFeedURL({ provider: 'github', owner: 'your-username', repo: 'your-repo' })
2. 自定义服务器
javascript// main.js autoUpdater.setFeedURL({ url: 'https://your-server.com/updates', headers: { 'Authorization': 'Bearer your-token' } })
3. S3 存储
json// package.json { "build": { "publish": { "provider": "s3", "bucket": "your-bucket-name", "path": 'updates' } } }
更新事件监听
javascript// main.js // 检查到更新 autoUpdater.on('update-available', (info) => { console.log('Update available:', info.version) sendStatusToWindow('Update available') }) // 更新已下载 autoUpdater.on('update-downloaded', (info) => { console.log('Update downloaded:', info.version) sendStatusToWindow('Update downloaded') // 提示用户重启应用 dialog.showMessageBox(mainWindow, { type: 'info', title: 'Update Available', message: 'A new version has been downloaded. Restart the application to apply the update.', buttons: ['Restart', 'Later'] }).then((result) => { if (result.response === 0) { autoUpdater.quitAndInstall() } }) }) // 更新不可用 autoUpdater.on('update-not-available', (info) => { console.log('Update not available') sendStatusToWindow('Update not available') }) // 更新错误 autoUpdater.on('error', (err) => { console.error('Update error:', err) sendStatusToWindow('Update error: ' + err.message) }) // 下载进度 autoUpdater.on('download-progress', (progressObj) => { let log_message = "Download speed: " + progressObj.bytesPerSecond log_message = log_message + ' - Downloaded ' + progressObj.percent + '%' log_message = log_message + ' (' + progressObj.transferred + "/" + progressObj.total + ')' sendStatusToWindow(log_message) }) function sendStatusToWindow(text) { mainWindow.webContents.send('update-status', text) }
渲染进程处理
javascript// renderer.js const { ipcRenderer } = require('electron') // 监听更新状态 ipcRenderer.on('update-status', (event, message) => { console.log('Update status:', message) updateStatusElement.textContent = message }) // 手动检查更新 document.getElementById('check-update').addEventListener('click', () => { ipcRenderer.send('check-for-updates') }) // 主进程处理 ipcMain.on('check-for-updates', () => { autoUpdater.checkForUpdates() })
高级配置
1. 定时检查更新
javascript// main.js const CHECK_UPDATE_INTERVAL = 24 * 60 * 60 * 1000 // 24小时 app.whenReady().then(() => { // 应用启动时检查 autoUpdater.checkForUpdatesAndNotify() // 定时检查 setInterval(() => { autoUpdater.checkForUpdates() }, CHECK_UPDATE_INTERVAL) })
2. 静默更新
javascript// main.js autoUpdater.autoDownload = true autoUpdater.autoInstallOnAppQuit = true app.on('before-quit', () => { if (autoUpdater.isUpdateDownloaded()) { autoUpdater.quitAndInstall() } })
3. 自定义更新检查
javascript// main.js async function checkForUpdates() { try { const updateCheckResult = await autoUpdater.checkForUpdates() if (updateCheckResult.updateInfo.version !== app.getVersion()) { // 有新版本 return { hasUpdate: true, version: updateCheckResult.updateInfo.version, releaseNotes: updateCheckResult.updateInfo.releaseNotes } } else { // 没有新版本 return { hasUpdate: false } } } catch (error) { console.error('Update check failed:', error) return { hasUpdate: false, error: error.message } } }
版本管理
1. 版本号格式
json// package.json { "version": "1.0.0" }
遵循语义化版本规范(SemVer):
- 主版本号.次版本号.修订号 (MAJOR.MINOR.PATCH)
- 例如: 1.0.0, 1.2.3, 2.0.0
2. 发布新版本
bash# 更新版本号 npm version patch # 1.0.0 -> 1.0.1 npm version minor # 1.0.0 -> 1.1.0 npm version major # 1.0.0 -> 2.0.0 # 构建应用 npm run build # 发布到 GitHub git push --follow-tags
3. 发布说明
在 GitHub Releases 中添加发布说明:
markdown## Version 1.1.0 ### New Features - Added feature X - Added feature Y ### Bug Fixes - Fixed bug A - Fixed bug B ### Improvements - Improved performance - Enhanced user experience
安全考虑
1. 验证更新包
javascript// main.js autoUpdater.on('update-downloaded', (info) => { // 验证更新包签名 if (verifyUpdateSignature(info)) { autoUpdater.quitAndInstall() } else { console.error('Update signature verification failed') } }) function verifyUpdateSignature(info) { // 实现签名验证逻辑 return true }
2. 使用 HTTPS
javascript// main.js autoUpdater.setFeedURL({ url: 'https://your-server.com/updates', headers: { 'Authorization': 'Bearer your-token' } })
3. 限制更新频率
javascript// main.js let lastUpdateCheck = 0 const UPDATE_CHECK_INTERVAL = 24 * 60 * 60 * 1000 // 24小时 async function checkForUpdatesWithRateLimit() { const now = Date.now() if (now - lastUpdateCheck < UPDATE_CHECK_INTERVAL) { console.log('Update check skipped - too soon') return } lastUpdateCheck = now return await autoUpdater.checkForUpdates() }
错误处理
1. 网络错误
javascript// main.js autoUpdater.on('error', (err) => { if (err.message.includes('ERR_INTERNET_DISCONNECTED')) { console.error('Network disconnected') sendStatusToWindow('Network disconnected. Please check your internet connection.') } else if (err.message.includes('ERR_CONNECTION_REFUSED')) { console.error('Connection refused') sendStatusToWindow('Cannot connect to update server.') } else { console.error('Update error:', err) sendStatusToWindow('Update failed: ' + err.message) } })
2. 磁盘空间不足
javascript// main.js autoUpdater.on('error', (err) => { if (err.message.includes('ENOSPC')) { console.error('No disk space') sendStatusToWindow('Not enough disk space to download update.') } })
最佳实践
1. 用户体验
javascript// main.js // 在应用空闲时检查更新 app.on('ready', () => { setTimeout(() => { autoUpdater.checkForUpdatesAndNotify() }, 5000) // 延迟5秒,避免影响启动速度 }) // 提供更新进度反馈 autoUpdater.on('download-progress', (progress) => { const percentage = Math.round(progress.percent) sendStatusToWindow(`Downloading update: ${percentage}%`) })
2. 回滚机制
javascript// main.js // 保留旧版本 const { app } = require('electron') app.on('before-quit', () => { // 在更新前备份当前版本 const currentVersion = app.getVersion() const backupPath = path.join(app.getPath('userData'), 'backup', currentVersion) // 实现备份逻辑 })
3. 测试更新
javascript// 开发环境测试 if (process.env.NODE_ENV === 'development') { autoUpdater.setFeedURL({ url: 'http://localhost:3000/updates' }) }
常见问题
Q: 如何实现增量更新?A: electron-updater 默认支持增量更新,只需确保服务器配置正确,使用相同的发布流程即可。
Q: 更新失败后如何重试?A: 监听 error 事件,实现重试逻辑:
javascriptlet retryCount = 0 const MAX_RETRIES = 3 autoUpdater.on('error', (err) => { if (retryCount < MAX_RETRIES) { retryCount++ setTimeout(() => { autoUpdater.checkForUpdates() }, 5000 * retryCount) } })
Q: 如何跳过某个版本?A: 在应用设置中保存跳过的版本号,检查更新时进行比较:
javascriptconst skippedVersion = getSkippedVersion() if (newVersion !== skippedVersion) { // 显示更新提示 }
Q: 更新后如何迁移用户数据?A: 在主进程中监听 before-quit 事件,在更新前执行数据迁移逻辑。