# Tauri v2 客户端自动更新指南 > **方案版本**: v1.0(2026-04-02) > **适用范围**: Tauri v2 + 任意更新源(阿里云 OSS / GitHub Releases / 自建服务器) > **验证状态**: 已在 dev-manager-tauri 项目实际跑通 > > 本文档聚焦客户端侧的自动更新功能实现,涵盖从插件安装到 UI 交互的全链路。 > 服务端构建与发布流程请参考 [tauri-cicd-guide.md](./tauri-cicd-guide.md)。 --- ## 整体流程 ``` 客户端启动 / 用户点击「检查更新」 │ ▼ 请求 endpoint 上的 latest.json │ ▼ 比较 latest.json.version vs 本地版本 │ ┌────┴────┐ │ 无更新 │ 有新版本 │ 结束 │ │ ▼ │ 展示版本号 + 更新说明 │ 用户点击「立即更新」 │ │ │ ▼ │ 下载 .exe(带进度) │ │ │ ▼ │ 验证签名 → 安装 → relaunch └─────────┘ ``` --- ## 一、安装依赖 ### 1.1 Rust 端(src-tauri/Cargo.toml) ```toml [dependencies] tauri-plugin-updater = "2" tauri-plugin-process = "2" ``` ### 1.2 前端(package.json) ```bash pnpm add @tauri-apps/plugin-updater @tauri-apps/plugin-process ``` --- ## 二、注册插件 `src-tauri/src/lib.rs` 中注册两个插件: ```rust tauri::Builder::default() .plugin(tauri_plugin_updater::Builder::new().build()) .plugin(tauri_plugin_process::init()) // ... 其他插件和配置 ``` - `tauri_plugin_updater` — 检查更新、下载、验证签名、安装 - `tauri_plugin_process` — 安装后调用 `relaunch()` 重启应用 --- ## 三、配置 tauri.conf.json ```jsonc { "bundle": { "createUpdaterArtifacts": true // ⚠️ 必须!否则构建不生成 .exe.sig 签名文件 }, "plugins": { "updater": { "pubkey": "<公钥 base64>", "endpoints": [ "https://./latest.json" ], "dialog": false // false = 用代码控制更新 UI;true = 系统原生弹窗 } } } ``` ### 关键说明 | 字段 | 作用 | 注意事项 | |------|------|---------| | `createUpdaterArtifacts` | 构建时生成 `.exe.sig` 签名文件 | Tauri v2 默认 false,不开启则签名静默跳过 | | `pubkey` | 客户端用来验证下载文件签名 | 是**公钥文件**的 base64,不是私钥 | | `endpoints` | `latest.json` 的 URL 列表 | 可配多个,按顺序尝试 | | `dialog` | 是否使用原生弹窗 | 推荐 false,自定义 UI 体验更好 | ### pubkey 生成方法 ```powershell # 生成密钥对 pnpm tauri signer generate -w "$HOME\.tauri\your-app.key" # 获取公钥 base64(填入 pubkey 字段) $content = Get-Content "$HOME\.tauri\your-app.key.pub" -Raw $content = $content -replace "`r`n", "`n" $bytes = [System.Text.Encoding]::UTF8.GetBytes($content) [Convert]::ToBase64String($bytes) ``` --- ## 四、配置 Capabilities 权限 `src-tauri/capabilities/default.json` 中必须声明权限,否则前端 API 调用会被拦截: ```jsonc { "identifier": "default", "windows": ["main"], "permissions": [ "core:default", // ... 其他权限 ... { "identifier": "http:default", "allow": [ { "url": "https://*.aliyuncs.com/**" } // ⚠️ 放行更新源域名 ] }, "updater:default", // ⚠️ 允许 check()、downloadAndInstall() "process:default" // ⚠️ 允许 relaunch() ] } ``` **三项缺一不可**: - `updater:default` — 否则报 `updater.check not allowed` - `process:default` — 否则 `relaunch()` 无法调用 - http allow 列表包含更新源域名 — 否则报 `Could not fetch a valid release JSON` --- ## 五、latest.json 格式 更新源需要提供一个 `latest.json`,格式如下: ```json { "version": "0.1.11", "pub_date": "2026-04-02T05:29:44Z", "notes": "版本 v0.1.11 已发布", "platforms": { "windows-x86_64": { "signature": "<.exe.sig 文件内容>", "url": "https://./releases/v0.1.11/_0.1.11_x64-setup.exe" } } } ``` | 字段 | 说明 | |------|------| | `version` | 新版本号,updater 用它和本地版本比较 | | `pub_date` | ISO 8601 格式,必须有 | | `notes` | 更新说明,前端通过 `update.body` 获取 | | `platforms` | 按平台提供下载链接和签名 | | `signature` | `.exe.sig` 文件内容(base64),客户端用 pubkey 验证 | | `url` | 安装包下载地址,必须可公开访问 | ### 多平台支持 ```json { "platforms": { "windows-x86_64": { "signature": "...", "url": "...setup.exe" }, "darwin-x86_64": { "signature": "...", "url": "...app.tar.gz" }, "darwin-aarch64": { "signature": "...", "url": "...app.tar.gz" }, "linux-x86_64": { "signature": "...", "url": "...AppImage.tar.gz" } } } ``` --- ## 六、前端实现(React + TypeScript) ### 6.1 状态机设计 更新流程有明确的状态流转,用联合类型建模最清晰: ```typescript type UpdateState = | { status: "idle" } // 初始状态 | { status: "checking" } // 正在检查 | { status: "latest" } // 已是最新 | { status: "available"; version: string; notes: string; downloadAndInstall: () => Promise } | { status: "downloading"; progress: number } // 下载中(0~100) | { status: "error"; message: string } // 出错 ``` ### 6.2 完整组件参考 ```typescript import { useState, useEffect } from "react"; import { check as checkUpdate } from "@tauri-apps/plugin-updater"; import { relaunch } from "@tauri-apps/plugin-process"; import { getVersion } from "@tauri-apps/api/app"; function UpdateSection() { const [appVersion, setAppVersion] = useState(""); const [state, setState] = useState({ status: "idle" }); useEffect(() => { getVersion().then(setAppVersion).catch(() => {}); }, []); const handleCheck = async () => { setState({ status: "checking" }); try { const update = await checkUpdate(); if (!update?.available) { setState({ status: "latest" }); return; } setState({ status: "available", version: update.version, notes: update.body ?? "", downloadAndInstall: async () => { let downloaded = 0; let total = 0; await update.downloadAndInstall((event) => { if (event.event === "Started") { total = event.data.contentLength ?? 0; setState({ status: "downloading", progress: 0 }); } else if (event.event === "Progress") { downloaded += event.data.chunkLength; const progress = total > 0 ? Math.round((downloaded / total) * 100) : 0; setState({ status: "downloading", progress }); } else if (event.event === "Finished") { setState({ status: "idle" }); } }); await relaunch(); }, }); } catch (e) { setState({ status: "error", message: String(e) }); } }; return (

当前版本 v{appVersion}

{state.status === "latest" && 已是最新版本} {state.status === "available" && ( <> v{state.version} 可用 )} {state.status === "downloading" && ( 下载中 {state.progress}% )} {state.status === "error" && (

{state.message}

)} {state.status !== "available" && ( )}
); } ``` ### 6.3 下载事件说明 `downloadAndInstall` 的回调事件顺序: | 事件 | 数据 | 说明 | |------|------|------| | `Started` | `{ contentLength?: number }` | 开始下载,contentLength 可能为 0 | | `Progress` | `{ chunkLength: number }` | 每个数据块,累加计算进度 | | `Finished` | 无 | 下载完成,随后自动安装 | > `downloadAndInstall` 完成后立即调用 `relaunch()` 重启应用,新版本生效。 --- ## 七、版本号管理 Tauri v2 项目有**三处版本号**必须保持一致: | 文件 | 字段 | 作用 | |------|------|------| | `src-tauri/tauri.conf.json` | `"version"` | updater 用来和 latest.json 比较 | | `src-tauri/Cargo.toml` | `version` | Rust 构建元数据 | | `package.json` | `"version"` | 前端构建元数据 | ### 发版步骤 ```bash # 1. 三处改版本号(如 0.2.0) # 2. 提交 git add -A git commit -m "release: v0.2.0" # 3. 打 tag + push(PowerShell 不支持 &&,分开执行) git tag v0.2.0 git push git push --tags ``` > **版本号不一致的后果**:`latest.json` 中的 version 高于本地版本时才会提示更新。 > 如果 `tauri.conf.json` 的版本没改,每次启动都会提示"有新版本"即使已经更新过。 --- ## 八、更新源选择 | 更新源 | 优点 | 缺点 | 适用场景 | |--------|------|------|---------| | **阿里云 OSS** | 国内快,CDN 加速 | 需要额外配置 OSS | 面向国内用户 | | **GitHub Releases** | 零配置,tauri-action 自动生成 | 国内访问慢/不稳定 | 面向海外用户 | | **自建服务器** | 完全可控 | 需要运维 | 企业内网分发 | Tauri v2 的 `endpoints` 支持多个 URL,可以配置主备: ```json "endpoints": [ "https://.oss-cn-hangzhou.aliyuncs.com/latest.json", "https://github.com///releases/latest/download/latest.json" ] ``` --- ## 九、调试技巧 ### 9.1 验证 latest.json 可访问 ```powershell curl https:///latest.json ``` - 返回 JSON → 正常 - 返回 `AccessDenied` → OSS Bucket 未设为公共读 - 返回 404 → CI/CD 未上传或路径不对 ### 9.2 开发模式测试 `pnpm tauri dev` 模式下也能调用 `check()`,但注意: - 开发模式的版本号来自 `tauri.conf.json` - 如果本地版本号和 latest.json 相同或更高,不会触发更新 ### 9.3 强制触发更新(调试用) 临时把 `tauri.conf.json` 的版本号改低(如 `0.0.1`),再运行 `pnpm tauri dev` 点检查更新即可触发。 --- ## 十、踩坑清单 | # | 问题 | 现象 | 解决方案 | |---|------|------|---------| | 1 | 缺少 `updater:default` | `updater.check not allowed` | `capabilities/default.json` 添加 `updater:default` | | 2 | 缺少 `process:default` | `relaunch()` 无效 | `capabilities/default.json` 添加 `process:default` | | 3 | http 未放行更新源域名 | `Could not fetch a valid release JSON` | capabilities http allow 加更新源域名 | | 4 | OSS Bucket 为私有 | 同上,curl 返回 `AccessDenied` | Bucket ACL 改为**公共读** | | 5 | `createUpdaterArtifacts` 未开启 | 构建成功但无 `.exe.sig` | `tauri.conf.json` → `bundle.createUpdaterArtifacts: true` | | 6 | pubkey 填了私钥 | `failed to decode pubkey` | 用公钥文件的 base64 | | 7 | 版本号三处不一致 | 更新后仍提示有新版本 | `tauri.conf.json`、`Cargo.toml`、`package.json` 必须同步 | | 8 | latest.json 缺少 `pub_date` | updater 解析失败 | 必须包含 ISO 8601 格式的日期 | | 9 | signature 字段为空 | 签名验证失败 | 检查 CI/CD 是否正确读取了 `.exe.sig` 文件内容 | | 10 | `dialog: true` 时无法自定义 UI | 弹出系统原生对话框 | 设为 `false`,用代码实现自定义更新界面 | --- ## 配置文件速查 新项目最小配置清单: ``` src-tauri/Cargo.toml → 添加 tauri-plugin-updater、tauri-plugin-process 依赖 package.json → pnpm add @tauri-apps/plugin-updater @tauri-apps/plugin-process src-tauri/src/lib.rs → 注册两个插件 src-tauri/tauri.conf.json → bundle.createUpdaterArtifacts + plugins.updater 配置 src-tauri/capabilities/*.json → updater:default + process:default + http allow 前端组件 → 调用 check() + downloadAndInstall() + relaunch() ```