自动更新 #

一、更新概述 #

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