从 WPF 到 Tauri:桌面应用开发、调试、构建与发布完整入门

从 WPF 到 Tauri:桌面应用开发、调试、构建与发布完整入门

本文写给熟悉 WPF、C#、Visual Studio 和 MSBuild,但第一次接触 Tauri、Vue、TypeScript、Rust、Vite 与 GitHub Actions 的桌面开发者。

文中的项目名、仓库地址和业务代码均为通用示例,可以直接迁移到自己的项目中。

前言

刚接触 Tauri 项目时,WPF 开发者往往会遇到一连串困惑:

  • 没有 .sln 和 .csproj,项目应该从哪里打开?
  • 应该使用 Visual Studio、VS Code,还是其他 IDE?
  • Vue、TypeScript、Rust、Node.js 和 Tauri 分别负责什么?
  • 为什么运行程序要输入命令,而不是直接按 Visual Studio 的绿色三角?
  • CodeLLDB、tasks.json 和 launch.json 分别有什么用?
  • 为什么源码目录只有一点点代码,整个项目却占用几十 GB?
  • 为什么 Tauri 安装包很小,又不需要用户安装 Node.js 和 Rust?
  • GitHub 为什么能自动生成 Windows 和 macOS 安装包?每次推送都会发布吗?

这些问题的根源是:WPF 把编辑、构建、包管理、调试和发布高度集成在 Visual Studio 中,而 Tauri 把多个成熟生态组合在一起。理解每一层的职责后,它并不比 WPF 神秘,只是项目组织方式不同。


一、先建立整体模型

一个常见的 Tauri 桌面应用可以理解为:

Tauri 桌面应用
├─ 前端:HTML + CSS + JavaScript
│  └─ 通常使用 Vue、React、Svelte 等框架开发
│
├─ 原生端:Rust
│  ├─ 创建窗口
│  ├─ 操作系统托盘
│  ├─ 文件读写
│  ├─ 系统通知
│  ├─ 后台任务
│  └─ 调用操作系统 API
│
└─ Tauri
   ├─ 管理桌面窗口和 WebView
   ├─ 连接 JavaScript 与 Rust
   ├─ 提供权限和插件机制
   └─ 生成 EXE、安装包、DMG 等产物

这里的“前端”和“后端”都运行在用户电脑上,不代表一定存在远程服务器。

可以先用 WPF 做一个职责对照:

WPF/.NET Tauri + Vue + Rust
WPF 应用框架 Tauri
XAML Vue 的 <template>
ResourceDictionary、Style CSS
ViewModel、部分 Code-behind Vue 的 <script setup lang="ts">
Binding v-model、:属性、{{ 文本 }}
Click、ICommand @click 调用 TypeScript 函数
C# Service Rust command
C# 事件、消息总线 Tauri event
NuGet pnpm 和 Cargo
MSBuild Vite、Cargo、Tauri CLI
Visual Studio 调试器 浏览器开发者工具、CodeLLDB 或 Windows Debugger

这个对照只是帮助建立概念,并不表示两者实现机制完全一样。


二、Windows 和 macOS 上如何显示网页界面

Tauri 不会像 Electron 那样把一整套 Chromium 和 Node.js 打进每个应用,而是复用操作系统提供的 WebView。

操作系统 Tauri 使用的 WebView 浏览器内核
Windows Microsoft Edge WebView2 Chromium
macOS WKWebView WebKit
Linux WebKitGTK WebKit

因此:

  • Windows 通常使用系统安装或更新的 WebView2。
  • macOS 直接使用系统自带的 WKWebView。
  • Linux 依赖系统中的 WebKitGTK。

这正是 Tauri 安装包通常比 Electron 小很多的重要原因。

同一套 HTML、CSS 和 JavaScript 在 WebView2 与 WKWebView 中可能存在少量差异,例如字体渲染、滚动条、日期输入框、新 CSS 特性和系统控件外观。因此,发布 macOS 版本前仍然需要在真实 Mac 或对应测试环境中验证。

调试前端界面时通常使用:

Windows:Ctrl + Shift + I
macOS:Command + Option + I

它们分别打开 WebView2 或 WKWebView 的开发者工具。


三、为什么 Tauri 不需要打包 Node.js 和 Rust 运行时

这是 WPF 开发者最关心的问题之一。

3.1 .NET 自包含为什么比较大

典型的 .NET Framework-dependent 发布依赖目标电脑已经安装匹配的 .NET Runtime。

如果使用 Self-contained 发布,则需要携带:

业务程序集
CoreCLR
.NET 基础类库
JIT 及相关原生组件
运行时依赖

因此产物体积明显增加。

3.2 Rust 为什么不需要额外运行时

Rust 使用提前编译:

Rust 源码
    ↓ rustc
Windows/macOS/Linux 原生机器码

Rust 程序通常不依赖一个类似 CoreCLR、JVM 或 Node.js 的大型通用运行时。编译器将程序需要的代码编译进最终二进制,同时继续调用操作系统提供的动态库和 API。

Rust 不是“一次编译,到处运行”。不同平台需要分别构建:

Windows x64      → Windows x64 EXE
macOS ARM64      → Apple Silicon 应用
macOS x64        → Intel Mac 应用
Linux x64        → Linux x64 可执行文件

3.3 Node.js 为什么不用进入安装包

Node.js 在常见的 Vue + Tauri 项目中主要用于开发和构建:

Vue/TypeScript 源码
        ↓ Node.js 运行 Vite
HTML + CSS + JavaScript 静态文件

正式运行时,JavaScript 由系统 WebView 执行,不由 Node.js 执行,所以安装包不需要携带 Node.js。

开发模式和正式模式的区别如下。

开发模式:

Node.js 启动 Vite 开发服务器
        ↓
提供 http://localhost:某端口
        ↓
Tauri 窗口中的 WebView 加载该地址
        ↓
支持热更新

正式构建:

Node.js 运行 Vite
        ↓
输出 dist 中的 HTML/CSS/JavaScript
        ↓
Tauri 将静态资源嵌入桌面应用
        ↓
用户电脑直接运行,不需要 Vite 和 Node.js

可以把这些工具分成两组:

只在开发和构建时使用 最终用户运行时使用
Node.js Rust 编译后的原生程序
pnpm 系统 WebView
Vite 嵌入应用的 HTML/CSS/JavaScript
TypeScript 编译器 操作系统 API 和动态库
Cargo 业务数据和应用资源
rustc

3.4 为什么 Debug 目录很大,安装包却很小

Debug 构建会包含大量调试信息,并生成 PDB、增量编译缓存和依赖中间文件。Rust 的泛型实例化以及多个构建配置,也会使 target 目录快速增长到数 GB甚至数十 GB。

Release 构建则可以开启优化和符号裁剪:

[profile.release]
opt-level = 3
lto = true
codegen-units = 1
panic = "abort"
strip = true

因此应区分:

项目构建缓存很大 ≠ 发给用户的安装包很大

四、Vue 和 Rust 分别负责什么

4.1 Vue/TypeScript 的职责

Vue 不只是“画界面”,通常还承担界面层和交互层职责:

  • 页面布局和组件组织。
  • 用户输入和双向绑定。
  • 页面切换。
  • 表单临时状态。
  • 界面级输入校验。
  • 列表排序和显示格式化。
  • 主题、颜色、语言切换。
  • 加载状态和进度显示。
  • 点击、选择、展开、关闭等交互。
  • 将用户操作转换为对 Rust 的调用。

一个简单组件可能写成:

<script setup lang="ts">
import { ref } from 'vue'
import { invoke } from '@tauri-apps/api/core'

const title = ref('')
const message = ref('')

async function save() {
  await invoke('save_item', { title: title.value })
  message.value = '保存成功'
}
</script>

<template>
  <input v-model="title" />
  <button @click="save">保存</button>
  <span>{{ message }}</span>
</template>

和 WPF 对照:

v-model       ≈ 双向 Binding
@click        ≈ Click 或 ICommand
ref/reactive  ≈ 可通知界面的状态属性
computed      ≈ 根据其他状态计算的属性

4.2 Rust 的职责

Rust 更适合承担需要原生能力、后台可靠性和安全边界的工作,例如:

  • 文件读写和配置存储。
  • 数据校验和规范化。
  • 数据库访问。
  • 后台定时任务。
  • 系统托盘。
  • 原生窗口管理。
  • 系统通知。
  • 开机自启。
  • 单实例运行。
  • 调用操作系统 API。
  • 启动外部进程。
  • 下载文件和校验哈希。
  • 自动更新。
  • 权限敏感操作。
  • CPU 密集型计算。
  • 与已有 C/C++ 动态库进行 FFI 调用。

Rust 端可以暴露命令:

#[tauri::command]
fn save_item(title: String) -> Result<(), String> {
    if title.trim().is_empty() {
        return Err("标题不能为空".to_string());
    }

    // 保存数据……
    Ok(())
}

并在 Tauri 初始化时注册:

.invoke_handler(tauri::generate_handler![save_item])

4.3 Vue 与 Rust 如何通信

Vue 调用 Rust 通常使用 invoke:

Vue/TypeScript
    ↓ invoke("save_item", 参数)
Tauri IPC
    ↓
Rust command

Rust 主动通知 Vue 通常使用事件:

Rust 后台任务检测到事件
    ↓ emit
Tauri IPC
    ↓ listen
Vue 更新界面

调用方向是双向的:

Vue → Rust:命令调用
Rust → Vue:事件通知

需要注意,Vue 对象与 Rust 对象不在同一块内存中。参数会被序列化后跨越 WebView 与原生端边界,因此 TypeScript interface 和 Rust struct 需要保持字段兼容。

4.4 为什么 Tauri 选择 Rust,而不是 C++

从架构上说,C++ 当然也能创建原生窗口、托管 WebView2、调用系统 API。但是 Tauri 的核心生态以 Rust 为中心。

Rust 的主要优势是:

  • 编译成原生机器码。
  • 没有 GC 暂停。
  • 通过所有权和借用检查减少悬空指针、越界和 use-after-free。
  • 对多线程共享数据有较强的编译期约束。
  • Cargo 统一处理依赖、构建和测试。
  • Tauri 的窗口、托盘、插件、权限和 IPC 能力可直接使用。
  • 跨平台条件编译比较统一。

如果业务已经有成熟 C++ 库,可以通过 Rust FFI 调用 C++,也可以把 C++ 程序作为 sidecar 子进程。若完全不想使用 Rust,则通常需要选择其他桌面框架,而不是继续使用标准 Tauri 后端。


五、典型 Tauri + Vue 项目目录

一个常见项目大致如下:

desktop-app/
├─ .github/
│  └─ workflows/              GitHub Actions 工作流
├─ .vscode/                   VS Code 项目配置
├─ src/                       Vue/TypeScript 前端源码
│  ├─ assets/                 前端图片和资源
│  ├─ components/             Vue 组件
│  ├─ composables/            可复用的 Vue 组合逻辑
│  ├─ App.vue                 根组件
│  ├─ main.ts                 前端入口
│  ├─ i18n.ts                 项目自定义的多语言模块
│  ├─ types.ts                TypeScript 类型
│  └─ style.css               样式
├─ src-tauri/                 Tauri/Rust 原生项目
│  ├─ capabilities/           Tauri 权限声明
│  ├─ icons/                  应用及安装包图标
│  ├─ src/
│  │  ├─ main.rs              Rust 可执行程序入口
│  │  └─ lib.rs               Rust 库入口和主要逻辑
│  ├─ build.rs                Cargo 构建脚本
│  ├─ Cargo.toml              Rust 项目和依赖配置
│  ├─ Cargo.lock              Rust 依赖锁定文件
│  └─ tauri.conf.json         Tauri 窗口、构建和打包配置
├─ dist/                      前端构建产物
├─ node_modules/              JavaScript 依赖
├─ index.html                 Vite HTML 入口
├─ package.json               前端依赖和脚本
├─ pnpm-lock.yaml             pnpm 依赖锁定文件
├─ tsconfig.json              TypeScript 配置
└─ vite.config.ts             Vite 配置

其中:

  • src/ 是 Vue/Vite 的常见约定,不是 Tauri 强制要求。
  • src-tauri/ 是 Tauri 的典型默认目录。
  • dist/ 是 Vite 常见的输出目录,可以通过配置修改。
  • node_modules/ 和 src-tauri/target/ 都是可重新生成的依赖或构建目录。

某些仓库还会有:

site/       宣传网站
scripts/    构建或发布辅助脚本
tests/      测试
docs/       文档

这些不是 Tauri 强制目录,而是项目自身的组织方式。site/ 通常表示与桌面程序放在同一仓库中的宣传网站,可以类比一个 Visual Studio 解决方案中同时包含桌面项目和网站项目。


六、哪些文件名固定,哪些只是约定

可以按三类理解。

6.1 工具要求或基本固定的名称

文件或目录 使用者
package.json Node.js 包管理生态
Cargo.toml Cargo
Cargo.lock Cargo
pnpm-lock.yaml pnpm
.github/workflows/ GitHub Actions
src-tauri/src/main.rs Cargo 默认二进制入口
src-tauri/src/lib.rs Cargo 默认库入口
src-tauri/build.rs Cargo 构建脚本

6.2 工具默认使用、但可以配置的名称

名称 说明
src/ 常见前端源码目录
dist/ 常见前端输出目录
index.html Vite 默认 HTML 入口
vite.config.ts Vite 配置
tsconfig.json TypeScript 配置
tauri.conf.json Tauri 默认配置文件
src-tauri/ Tauri 默认原生目录

这些通常可以通过参数或配置改变,但没有充分理由时不建议改,因为标准名称更容易被文档、扩展和其他开发者识别。

6.3 项目自己定义的名称

App.vue
types.ts
i18n.ts
error.ts
useUpdater.ts

这些文件都可以改名,但必须同步修改所有 import 和脚本引用。

例如:

import { translate } from './i18n'

如果把文件改成 translations.ts,引用也要改成:

import { translate } from './translations'

七、Vue 多语言为什么常见 i18n.ts

i18n 是 internationalization 的缩写,首尾字母之间有 18 个字母,所以行业中经常使用这个名称。

Vue 并不要求多语言文件必须叫 i18n.ts,也不要求必须采用某一种实现。

可选方案包括:

src/i18n.ts                  自定义 TypeScript 翻译模块
src/locales/zh-CN.json       JSON 语言资源
src/locales/en.json
src/resources/*.ts           按语言拆分 TS 模块
vue-i18n                     第三方国际化框架
服务端或数据库读取           动态语言资源
直接写在组件中               适合无需切换语言的小项目

和 WPF 的对应关系大致是:

WPF Vue
.resx JSON 或 TypeScript 语言资源
ResourceDictionary 语言对象或模块
自定义 XML 自定义 JSON/XML 读取逻辑
翻译服务 translate() 或 $t()
第三方本地化库 vue-i18n
XAML 中写死文字 template 中写死文字

小型双语应用采用自定义 i18n.ts 的好处是依赖少、逻辑直观,并且可以通过 TypeScript 对翻译键做类型检查。大型应用则更适合采用 vue-i18n 和拆分后的语言资源文件。


八、开发环境由哪些工具组成

Windows 上常见的 Tauri 开发环境包括:

Node.js
pnpm/npm/yarn
Rust stable
Cargo
MSVC C++ 构建工具
WebView2
VS Code 或其他 IDE

macOS 上则通常需要:

Node.js
pnpm/npm/yarn
Rust stable
Cargo
Xcode Command Line Tools
WKWebView(系统自带)
VS Code、RustRover 等 IDE

查看本机环境可以使用:

node --version
pnpm --version
rustc --version
cargo --version
pnpm exec tauri --version
pnpm exec tauri info

如果某个 AI 开发工具或集成环境能够编译,而普通 PowerShell 不能执行 node 或 pnpm,通常说明该工具在自己的进程环境中注入了内部运行时路径。

内部运行时技术上可以临时借用,但不适合作为长期正式开发环境,因为:

  • 缓存目录可能在升级后变化。
  • 清理缓存后可能消失。
  • 版本由工具管理,不由项目管理。
  • VS Code、Git hooks 和其他程序不一定能找到它。

长期开发应单独安装项目需要的 Node.js、pnpm 和 Rust 工具链,并确保它们位于用户或系统 PATH 中。


九、常用开发工具如何选择

9.1 VS Code

这是 Vue + Tauri 项目中非常常见的组合:

VS Code
├─ Vue (Official)
├─ rust-analyzer
├─ CodeLLDB
└─ Tauri 扩展

各插件职责如下:

插件 职责
Vue (Official) Vue、模板和 TypeScript 提示
rust-analyzer Rust 补全、检查、跳转和重构
CodeLLDB Rust 原生程序断点调试
Tauri 配置提示和部分辅助命令

9.2 RustRover

RustRover 更接近传统完整 IDE,对 Cargo、Rust 代码和调试的集成更统一。但 Vue 前端和 Tauri 的双进程开发流程仍然需要运行配置,不能理解为安装后完全零配置。

9.3 Visual Studio

Visual Studio 在 Windows Tauri 开发中通常承担:

  • 提供 MSVC 编译器和链接器。
  • 提供 Windows SDK。
  • 在需要时提供 Windows 原生调试器。

它通常不是 Vue + Tauri 项目的主要编辑器,但依然是 Windows 原生构建环境的重要组成部分。


十、编译器、任务和调试器不是同一件事

安装 CodeLLDB 后仍然可能需要 tasks.json 和 launch.json,因为它们职责不同:

Cargo/rustc       → 编译 Rust
Vite/vue-tsc      → 检查并构建前端
Tauri CLI         → 协调前端与 Rust,打包桌面应用
tasks.json        → 告诉 VS Code 先执行哪些构建命令
launch.json       → 告诉 VS Code 启动哪个程序、使用哪个调试器
CodeLLDB          → 程序启动后负责断点、单步、变量和调用栈

这和 Visual Studio 中由项目系统隐式完成的工作类似,只是 VS Code 将配置显式写在 JSON 文件中。

10.1 日常热更新入口

.vscode/launch.json 可以配置:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "桌面应用:开发运行(热更新)",
      "type": "node-terminal",
      "request": "launch",
      "command": "pnpm tauri:dev",
      "cwd": "${workspaceFolder}",
      "autoAttachChildProcesses": false
    }
  ]
}

这个入口适合日常改界面和观察效果:

  • Vue、TypeScript、CSS 通常支持热更新。
  • Rust 修改后 Tauri 会重新编译和重启。
  • 前端使用 WebView 开发者工具调试。
  • 默认不会自动附加 Rust 断点调试器。

10.2 Rust 断点调试入口

tasks.json 中先定义构建任务:

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "构建 Debug EXE",
      "type": "shell",
      "command": "pnpm",
      "args": ["tauri:build", "--debug", "--no-bundle"],
      "problemMatcher": [],
      "group": {
        "kind": "build",
        "isDefault": true
      }
    }
  ]
}

然后在 launch.json 中使用 CodeLLDB:

{
  "name": "桌面应用:Rust 断点调试",
  "type": "lldb",
  "request": "launch",
  "program": "${workspaceFolder}/src-tauri/target/debug/app.exe",
  "cwd": "${workspaceFolder}",
  "preLaunchTask": "构建 Debug EXE",
  "sourceLanguages": ["rust"],
  "env": {
    "RUST_BACKTRACE": "1"
  }
}

此时流程是:

按 F5
→ 执行 preLaunchTask
→ 构建前端静态资源
→ 编译 Rust Debug EXE
→ CodeLLDB 启动 EXE
→ Rust 断点生效

这个模式通常没有前端热更新,修改后需要停止并重新调试。

10.3 为什么常把两种模式分开

模式 前端热更新 Rust 断点
tauri dev 有 默认无
构建 Debug EXE + CodeLLDB 无 有

也可以配置为同时启动 Vite 和 CodeLLDB,但会增加端口管理、停止顺序、文件监听和重复启动的问题。初学阶段将二者分开更容易理解和排错。


十一、日常运行、检查与打包

11.1 首次安装依赖

pnpm install --frozen-lockfile

11.2 日常开发

pnpm tauri:dev

这相当于日常“运行查看效果”,不需要每次生成安装包。

11.3 前端检查

pnpm build
pnpm test

常见的前端构建脚本类似:

"build": "vue-tsc --noEmit && vite build"

含义是:

vue-tsc --noEmit  → 检查 Vue/TypeScript 类型
vite build        → 生成 dist 静态资源

11.4 Rust 检查和测试

cargo check --manifest-path .\src-tauri\Cargo.toml
cargo test --locked --manifest-path .\src-tauri\Cargo.toml

11.5 生成 Windows 安装包

pnpm tauri:build --bundles nsis

常见输出位置为:

src-tauri/target/release/app.exe
src-tauri/target/release/bundle/nsis/*-setup.exe

只有以下情况通常需要打包:

  • 测试正式 Release 行为。
  • 测试安装和升级。
  • 给别人分发。
  • 准备 GitHub Release。

普通修改和日常调试不需要反复生成安装包。


十二、为什么目录占用几十 GB,以及如何清理

最容易膨胀的是:

src-tauri/target/   Rust 编译缓存、依赖和调试符号
node_modules/       JavaScript 依赖
dist/               前端构建产物

可以安全重新生成的目录包括:

目录 是否可删除 恢复方式
src-tauri/target/ 可以 下次 Cargo/Tauri 构建重新生成
node_modules/ 可以 pnpm install --frozen-lockfile
dist/ 可以 pnpm build
src-tauri/gen/ 一般可以 Tauri 重新生成

不要删除:

src/
src-tauri/src/
package.json
pnpm-lock.yaml
Cargo.toml
Cargo.lock
tauri.conf.json
.git/

清理 Rust 构建缓存:

cargo clean --manifest-path .\src-tauri\Cargo.toml

清理 pnpm 无用的全局包缓存:

pnpm store prune

清理后第一次编译会变慢,这是正常现象。


十三、版本号与安装升级

Tauri 项目中版本号可能同时存在于:

package.json
src-tauri/Cargo.toml
src-tauri/tauri.conf.json

是否必须三个都一致,取决于项目和发布工作流。为了减少歧义,通常应保持一致:

package.json                 0.9.0
Cargo.toml                   0.9.0
tauri.conf.json              0.9.0
Git 标签                     v0.9.0

使用三段式语义版本:

0.8.0   正确
0.9.0   正确
0.9     不建议,某些发布脚本会直接拒绝

NSIS 安装包能否覆盖旧版本,通常取决于:

  • 应用 identifier 是否保持不变。
  • 安装模式是否保持不变。
  • 新版本号是否高于旧版本。
  • 旧程序是否已经退出。
  • 安装包和便携版是否被混淆。

便携版 EXE 没有标准安装升级过程,通常需要退出旧程序后手动替换文件。

NSIS 默认界面比较朴素,可以配置图标、顶部图片、侧边图片、语言、许可协议,也可以进一步使用自定义 NSIS 模板。完全自定义会提高构建和维护成本。


十四、GitHub Actions 如何自动构建和发布

GitHub 不会自动分析项目并猜测如何编译。它只会读取:

.github/workflows/*.yml

然后按照工作流中的 on: 条件决定何时执行。

14.1 普通推送不一定触发发布

一个典型桌面发布工作流可能写成:

name: Release

on:
  push:
    tags: ['v*']
  workflow_dispatch:

含义是:

  • 推送以 v 开头的 Git 标签时执行。
  • 也允许在 GitHub Actions 页面手动运行。

普通推送:

git push origin master

不会触发这个 Release 工作流。

仅仅把版本号从 0.8.0 改成 0.9.0 并推送,也不会触发。版本号是项目数据,真正的触发条件是标签事件。

14.2 正式发布的典型流程

git add .
git commit -m "发布 0.9.0"
git push origin master

git tag v0.9.0
git push origin v0.9.0

最后一条推送标签的命令匹配:

tags: ['v*']

随后 GitHub Actions 才开始构建。

发布脚本还可以校验:

package.json 版本        0.9.0
Cargo.toml 版本          0.9.0
tauri.conf.json 版本     0.9.0
Git 标签                 v0.9.0

如果推送了 v0.9.0,而代码中的版本仍是 0.8.0,工作流会被触发,但应在校验阶段失败。

14.3 Windows 和 macOS 为什么能同时构建

工作流会明确指定 Runner 和目标:

strategy:
  matrix:
    include:
      - os: windows-latest
        target: x86_64-pc-windows-msvc
        bundles: nsis

      - os: macos-latest
        target: aarch64-apple-darwin
        bundles: app,dmg

这不是 GitHub 自动识别出来的,而是工作流作者明确告诉 GitHub:

Windows Runner
→ 安装 Node、pnpm、Rust
→ 测试
→ 构建 Windows EXE 和 NSIS

macOS Runner
→ 安装 Node、pnpm、Rust
→ 测试
→ 构建 Apple Silicon App 和 DMG

所有构建成功后,工作流可以上传附件、生成更新清单,并将 Release 草稿转为公开版本。

14.4 网站发布可以使用不同触发条件

网站工作流可能只在网站文件发生变化时运行:

on:
  push:
    branches: [master]
    paths:
      - 'site/**'
      - '.github/workflows/pages.yml'

还可以在桌面 Release 成功后自动重建网站:

on:
  workflow_run:
    workflows: [Release]
    types: [completed]

最终形成:

普通业务代码推送
→ 不发布桌面安装包

网站代码推送
→ 更新 GitHub Pages

推送 v0.9.0 标签
→ 发布 Windows/macOS 安装包
→ Release 成功
→ 网站重新读取最新正式版本
→ 更新下载页面

十五、一套适合 WPF 开发者的日常流程

开发前

node --version
pnpm --version
cargo --version

日常修改和查看效果

pnpm tauri:dev

或者在 VS Code 中选择“开发运行(热更新)”后按 F5。

调试前端

打开 WebView 开发者工具
→ 查看 Console
→ 检查 Elements 和 CSS
→ 在 Sources 中设置 JavaScript/TypeScript 断点

调试 Rust

在 .rs 文件中设置断点
→ 选择 Rust 断点调试配置
→ F5
→ CodeLLDB 停在断点

也可以临时输出:

println!("开始保存");
dbg!(&data);

开启 Rust panic 调用栈:

$env:RUST_BACKTRACE = '1'
pnpm tauri:dev

提交代码前

pnpm build
pnpm test
cargo test --locked --manifest-path .\src-tauri\Cargo.toml
git status --short
git diff

需要安装包时

pnpm tauri:build --bundles nsis

准备正式发布时

同步版本号
→ 执行前端与 Rust 测试
→ 提交并推送代码
→ 创建 vX.Y.Z 标签
→ 推送标签
→ GitHub Actions 自动构建和发布

十六、从 WPF 迁移思维时最容易踩的坑

1. 把 tauri dev 当成完整断点调试

tauri dev 主要负责开发运行和热更新。Rust 断点还需要调试器和正确的启动配置。

2. 认为 CodeLLDB 会自动完成构建

CodeLLDB 是调试器,不是 Tauri 构建系统。构建顺序应由 task、Cargo、Vite 和 Tauri CLI 管理。

3. 每次修改都生成安装包

日常开发使用 tauri dev。安装包只在测试安装行为或准备交付时生成。

4. 把 node_modules 当成 Node.js 运行时

node_modules 是项目依赖,不是 Node.js 本体。Node.js 需要单独安装或由版本管理器提供。

5. 把 src-tauri/target 当成项目源码

它是编译缓存和产物,可以清理。真正的 Rust 源码位于 src-tauri/src。

6. 认为修改版本号就会触发 GitHub 发布

是否触发取决于 workflow 的 on: 配置。常见发布条件是推送 v* 标签,而不是检测文件内容变化。

7. 忽略开发版和安装版的单实例或数据目录冲突

如果开发版和安装版使用相同 identifier、单实例配置和数据目录,已经运行的安装版可能接管新进程,调试数据也可能与正式数据共用。调试前应退出托盘中的正式版本,并备份重要数据。


结语

WPF 与 Tauri 的核心差异,不在于一个“高级”、另一个“低级”,而在于工具链的组织方式:

WPF
Visual Studio 将项目、编译、调试、设计器和发布集中起来

Tauri
Vue/Vite 负责前端
Cargo/rustc 负责原生代码
Tauri CLI 负责协调和打包
VS Code/RustRover 负责编辑与调试体验
GitHub Actions 负责跨平台自动构建和发布

一旦把“编辑器、编译器、构建任务、调试器、运行时、WebView 和发布工作流”拆开理解,Tauri 项目就不再是一堆陌生目录,而是一条清晰的流水线:

编写 Vue 界面和 Rust 原生逻辑
→ 开发模式热更新
→ 分别调试前端和 Rust
→ 构建静态网页与原生程序
→ 打包各平台安装文件
→ 通过 Git 标签触发自动发布

对于已经熟悉 WPF 的开发者,真正需要学习的不是如何重新做桌面开发,而是如何把已有的界面、绑定、服务、事件、系统集成和发布经验,映射到 Vue、Rust、Tauri 和现代自动化工具链中。

posted @ 2026-09-24 11:44  吃饺子不沾醋  阅读(33)  评论(0)    收藏  举报