自动更新 #
一、更新概述 #
1.1 更新流程 #
text
┌─────────────────────────────────────────────────────────────┐
│ 更新流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 应用启动 ──► 检查更新 ──► 下载更新 ──► 安装更新 ──► 重启 │
│ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ │
│ 启动应用 请求服务器 下载文件 替换文件 完成更新 │
│ │
└─────────────────────────────────────────────────────────────┘
1.2 安装插件 #
bash
pnpm add @tauri-apps/plugin-updater
rust
// src-tauri/src/lib.rs
tauri::Builder::default()
.plugin(tauri_plugin_updater::Builder::new().build())
.run(tauri::generate_context!())
.expect("error while running tauri application");
二、更新配置 #
2.1 基本配置 #
json
// tauri.conf.json
{
"plugins": {
"updater": {
"active": true,
"endpoints": [
"https://releases.myapp.com/{{target}}/{{arch}}/{{current_version}}"
],
"pubkey": "your-public-key",
"windows": {
"installMode": "passive"
}
}
}
}
2.2 更新端点 #
json
{
"endpoints": [
"https://releases.myapp.com/{{target}}/{{arch}}/{{current_version}}",
"https://backup.myapp.com/{{target}}/{{arch}}/{{current_version}}"
]
}
2.3 安装模式 #
json
{
"windows": {
"installMode": "passive" // passive, basic, quiet
}
}
| 模式 | 说明 |
|---|---|
| passive | 显示进度但不需用户交互 |
| basic | 显示基本安装界面 |
| quiet | 完全静默安装 |
三、更新服务器 #
3.1 更新清单格式 #
json
{
"version": "1.1.0",
"notes": "Bug fixes and performance improvements",
"pub_date": "2024-01-15T12:00:00Z",
"platforms": {
"darwin-x86_64": {
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkK...",
"url": "https://releases.myapp.com/v1.1.0/myapp_x64.app.tar.gz"
},
"darwin-aarch64": {
"signature": "...",
"url": "https://releases.myapp.com/v1.1.0/myapp_aarch64.app.tar.gz"
},
"windows-x86_64": {
"signature": "...",
"url": "https://releases.myapp.com/v1.1.0/myapp_x64-setup.nsis.zip"
},
"linux-x86_64": {
"signature": "...",
"url": "https://releases.myapp.com/v1.1.0/myapp_amd64.AppImage.tar.gz"
}
}
}
3.2 静态文件服务器 #
text
releases/
├── latest.json
├── darwin-x86_64/
│ └── myapp.app.tar.gz
├── darwin-aarch64/
│ └── myapp.app.tar.gz
├── windows-x86_64/
│ └── myapp-setup.nsis.zip
└── linux-x86_64/
└── myapp.AppImage.tar.gz
3.3 GitHub Releases #
json
{
"endpoints": [
"https://github.com/user/repo/releases/latest/download/{{target}}-{{arch}}.json"
]
}
四、前端实现 #
4.1 检查更新 #
typescript
import { check } from '@tauri-apps/plugin-updater';
import { relaunch } from '@tauri-apps/plugin-process';
async function checkForUpdates() {
const update = await check();
if (update) {
console.log('Update available:', update.version);
return update;
}
console.log('No updates available');
return null;
}
4.2 下载并安装 #
typescript
import { check } from '@tauri-apps/plugin-updater';
import { relaunch } from '@tauri-apps/plugin-process';
async function updateApp() {
const update = await check();
if (!update) {
return false;
}
let downloaded = 0;
let contentLength = 0;
await update.downloadAndInstall((event) => {
switch (event.event) {
case 'Started':
contentLength = event.data.contentLength;
console.log(`Started downloading ${contentLength} bytes`);
break;
case 'Progress':
downloaded += event.data.chunkLength;
const progress = (downloaded / contentLength) * 100;
console.log(`Downloaded ${progress.toFixed(1)}%`);
break;
case 'Finished':
console.log('Download finished');
break;
}
});
console.log('Update installed, restarting...');
await relaunch();
}
4.3 React Hook #
typescript
import { useState, useCallback } from 'react';
import { check, Update } from '@tauri-apps/plugin-updater';
import { relaunch } from '@tauri-apps/plugin-process';
export function useUpdater() {
const [update, setUpdate] = useState<Update | null>(null);
const [downloading, setDownloading] = useState(false);
const [progress, setProgress] = useState(0);
const checkForUpdates = useCallback(async () => {
const result = await check();
setUpdate(result);
return result;
}, []);
const downloadAndInstall = useCallback(async () => {
if (!update) return;
setDownloading(true);
setProgress(0);
let downloaded = 0;
let contentLength = 0;
await update.downloadAndInstall((event) => {
switch (event.event) {
case 'Started':
contentLength = event.data.contentLength;
break;
case 'Progress':
downloaded += event.data.chunkLength;
setProgress((downloaded / contentLength) * 100);
break;
case 'Finished':
break;
}
});
setDownloading(false);
await relaunch();
}, [update]);
return {
update,
downloading,
progress,
checkForUpdates,
downloadAndInstall,
};
}
4.4 更新组件 #
tsx
import { useUpdater } from '../hooks/useUpdater';
export function UpdateNotification() {
const { update, downloading, progress, checkForUpdates, downloadAndInstall } = useUpdater();
useEffect(() => {
checkForUpdates();
}, []);
if (!update) return null;
return (
<div className="update-notification">
<p>发现新版本 {update.version}</p>
{downloading ? (
<div className="progress">
<div className="progress-bar" style={{ width: `${progress}%` }} />
</div>
) : (
<button onClick={downloadAndInstall}>立即更新</button>
)}
</div>
);
}
五、签名配置 #
5.1 生成密钥对 #
bash
# 生成私钥
tauri signer generate -w ~/.tauri/myapp.key
# 查看公钥
tauri signer sign --private-key-path ~/.tauri/myapp.key
5.2 配置签名 #
json
{
"plugins": {
"updater": {
"pubkey": "your-public-key-here"
}
}
}
5.3 构建时签名 #
bash
# 设置环境变量
export TAURI_SIGNING_PRIVATE_KEY="your-private-key"
export TAURI_SIGNING_PRIVATE_KEY_PASSWORD="your-password"
# 构建
pnpm tauri build
六、更新策略 #
6.1 自动检查 #
typescript
import { check } from '@tauri-apps/plugin-updater';
async function setupAutoCheck() {
// 启动时检查
await checkForUpdates();
// 定期检查(每小时)
setInterval(async () => {
await checkForUpdates();
}, 60 * 60 * 1000);
}
6.2 强制更新 #
typescript
async function checkForUpdates() {
const update = await check();
if (update && update.shouldUpdate) {
// 强制更新
await update.downloadAndInstall();
await relaunch();
}
}
6.3 增量更新 #
typescript
// 检查是否支持增量更新
if (update.supportsDeltaUpdate) {
// 使用增量更新
await update.downloadAndInstall();
} else {
// 完整更新
await update.downloadAndInstall();
}
七、最佳实践 #
7.1 更新通知 #
typescript
async function notifyUpdate(update: Update) {
const { ask } = await import('@tauri-apps/plugin-dialog');
const confirmed = await ask(
`发现新版本 ${update.version},是否立即更新?`,
{ title: '更新可用', kind: 'info' }
);
if (confirmed) {
await update.downloadAndInstall();
await relaunch();
}
}
7.2 错误处理 #
typescript
async function safeUpdate() {
try {
const update = await check();
if (!update) return;
await update.downloadAndInstall((event) => {
// 处理进度
});
await relaunch();
} catch (error) {
console.error('Update failed:', error);
// 显示错误信息
}
}
7.3 版本比较 #
typescript
function compareVersions(current: string, latest: string): number {
const currentParts = current.split('.').map(Number);
const latestParts = latest.split('.').map(Number);
for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
const currentPart = currentParts[i] || 0;
const latestPart = latestParts[i] || 0;
if (latestPart > currentPart) return 1;
if (latestPart < currentPart) return -1;
}
return 0;
}
八、总结 #
8.1 核心要点 #
| 要点 | 说明 |
|---|---|
| 插件安装 | tauri-plugin-updater |
| 更新配置 | endpoints 和 pubkey |
| 更新服务器 | 提供更新清单 |
| 签名验证 | 确保更新安全 |
| 更新流程 | 检查、下载、安装、重启 |
8.2 下一步 #
现在你已经掌握了自动更新,接下来让我们学习 代码签名,了解如何对应用进行签名!
最后更新:2026-03-28