📚 全栈开发学习系列
第 31 篇 · 阶段五:扩展与部署
✅ 阶段四:跨平台 App(26-30篇)
已完成
📌 阶段五:扩展与部署
进行中
✓ 31 Tauri 桌面应用入门
32 Docker 容器化
33 Nginx 反向代理
34 CI/CD 自动化部署
读完本篇你将能:
✓ 理解 Tauri 的架构原理与 Electron 的本质区别
✓ 使用 Tauri 2.x 脚手架创建第一个桌面应用
✓ 通过 Command 系统实现前端调用 Rust 后端
✓ 掌握事件系统实现后端向前端推送消息
✓ 了解 Tauri 插件生态与打包发布流程
Tauri 桌面应用入门:Rust + Web 轻量开发
全栈开发学习系列 · 第 31 篇 · 阶段五扩展层
📑 本篇目录
一、为什么选择 Tauri:与 Electron 的架构对比
二、环境准备:Rust + Node.js 双栈安装
三、项目初始化:Tauri 2.x 脚手架实战
四、Command 系统:前端调用 Rust 函数
五、事件系统:后端向前端推送消息
六、插件生态与系统能力调用
七、常见错误与三层练习
八、知识回顾与下一篇预告
一、为什么选择 Tauri:与 Electron 的架构对比
如果你用过 VS Code、Discord、Figma 这些桌面应用,大概率接触过 Electron——把 Chromium 浏览器和 Node.js 打包进应用,用 Web 技术写桌面软件。但 Electron 有个老毛病:一个简单的记事本应用动辄 100MB+ 起步,内存占用几百 MB,就像为了煮一杯茶搬来一整座锅炉房。
Tauri 走了另一条路:不打包浏览器,直接用操作系统自带的 WebView。Windows 用 WebView2,macOS 用 WKWebView,Linux 用 WebKitGTK。后端用 Rust 编写,通过 IPC 桥与前端通信。这个设计带来的直接好处是:包体积只有 Electron 的 1/10 ~ 1/20,内存占用降低 50% 以上。
| 对比维度 |
Electron |
Tauri 2.x |
| 渲染引擎 |
打包 Chromium(~80MB) |
系统 WebView(0 额外体积) |
| 后端语言 |
Node.js(JavaScript) |
Rust |
| 典型包体积 |
80 ~ 200 MB |
3 ~ 15 MB |
| 内存占用(空应用) |
~150 MB |
~30 MB |
| 跨平台 |
Win / macOS / Linux |
Win / macOS / Linux + iOS / Android |
| 学习曲线 |
前端开发者友好 |
需要 Rust 基础 |
💡 小贴士:Tauri 2.x 的重大变化
Tauri 1.x 只支持桌面端,2.x 起加入了 iOS 和 Android 支持,一套代码可以同时覆盖桌面和移动端。但本篇聚焦桌面开发,移动端能力作为拓展了解即可。
Tauri 的核心架构可以理解为三层:前端 WebView 层负责 UI 渲染,Rust 后端层负责业务逻辑和系统调用,中间通过IPC 桥进行安全通信。所有跨语言调用都经过安全沙箱校验,前端无法直接访问文件系统或网络,必须通过显式声明的 Command 或插件接口,这也是 Tauri 安全性更高的原因。
二、环境准备:Rust + Node.js 双栈安装
Tauri 需要两套环境:Rust 工具链用于编译后端,Node.js用于前端开发和构建。两个都装好了才能跑通完整流程。
2.1 Rust 安装
官方推荐用 rustup 管理 Rust 版本,一条命令搞定:
Bash - 安装 Rust 工具链
|
1
2
3
4
5
|
# macOS / Linux
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# 验证安装
rustc --version # rustc 1.8x.x
|
Windows 用户直接去 rust-lang.org 下载 rustup-init.exe 运行即可。安装完成后重启终端,确保 rustc 和 cargo 命令可用。
2.2 系统依赖
不同操作系统还需要额外的 WebView 编译依赖:
Bash - 各平台系统依赖
|
1
2
3
4
5
6
7
8
9
|
# macOS:安装 Xcode 命令行工具
xcode-select --install
# Ubuntu / Debian
sudo apt install libwebkit2gtk-4.1-dev build-essential \
curl wget file libssl-dev libgtk-3-dev libayatana-appindicator3-dev
# Windows:安装 WebView2 Runtime(Win11 自带)
# 下载 Microsoft Edge WebView2 Runtime 安装包
|
2.3 Node.js 安装
前端脚手架需要 Node.js 18+,推荐用 fnm 或 nvm 管理版本:
Bash - 安装 Node.js 20 LTS
|
1
2
3
4
5
|
# 使用 fnm 安装(推荐,跨平台)
fnm install 20
fnm default 20
node --version # v20.x.x
npm --version # 10.x.x
|
三、项目初始化:Tauri 2.x 脚手架实战
环境准备好后,用 Tauri 官方脚手架 create-tauri-app 一键生成项目。它支持多种前端框架模板:Vanilla、Vue、React、Svelte 等,本系列以 React + TypeScript 为主,就用这个组合。
Bash - 创建 Tauri 项目
|
1
2
3
4
5
6
|
# 交互式创建项目
npm create tauri-app@latest my-desktop-app
# 按提示选择:
# ? 前端框架:React
# ? 语言:TypeScript
|
3.1 项目结构解析
创建完成后,目录结构长这样:
Text - Tauri 项目结构
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
my-desktop-app/
├── src/ // 前端源码(React)
│ ├── App.tsx // 主组件
│ ├── main.tsx // 前端入口
│ └── ...
├── src-tauri/ // Rust 后端
│ ├── Cargo.toml // Rust 依赖配置
│ ├── tauri.conf.json // Tauri 配置文件
│ ├── icons/ // 应用图标
│ ├── capabilities/ // 权限配置(2.x 新增)
│ └── src/
│ ├── lib.rs // Tauri 插件注册
│ └── main.rs // 应用入口
├── package.json // 前端依赖
├── vite.config.ts // Vite 配置
└── tsconfig.json // TS 配置
|
关键文件有三个:tauri.conf.json 控制窗口大小、应用名称、权限等;src-tauri/src/main.rs 是 Rust 入口;src/App.tsx 是前端主组件。
3.2 启动开发模式
Tauri 的开发模式会同时启动 Vite 开发服务器和 Rust 编译,第一次启动因为要下载和编译 Rust 依赖会比较慢,耐心等几分钟:
Bash - 启动开发模式
|
1
2
3
4
|
cd my-desktop-app
npm install
npm run tauri dev
|
编译完成后会自动弹出一个桌面窗口,显示 Vite 的欢迎页面。恭喜你,第一个 Tauri 应用跑起来了!接下来我们看看前后端怎么通信。
四、Command 系统:前端调用 Rust 函数
Tauri 的前后端通信走 IPC 桥,但你不需要手动写底层协议。Tauri 封装了 Command 系统:在 Rust 端用 #[tauri::command] 宏标记函数,在前端用 invoke() 调用,参数和返回值自动序列化。
打个比方:Command 就像餐厅的菜单——后厨(Rust)把能做的菜(函数)列在菜单上(注册 command),顾客(前端)只能点菜单上有的菜,不能自己跑进厨房操作。这种显式声明的机制也是 Tauri 安全模型的核心。
4.1 定义 Rust Command
先在 Rust 端定义一个简单的加法函数,用宏标记为 command:
Rust - src-tauri/src/main.rs
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
// 1. 定义 Command 函数
#[tauri::command]
fn add(a: i32, b: i32) -> i32 {
a + b
}
// 2. 定义带错误处理的 Command
#[tauri::command]
fn greet(name: String) -> Result<String, String> {
if name.trim().is_empty() {
return Err("名字不能为空".to_string());
}
Ok(format!("你好,{}!来自 Rust 的问候", name))
}
// 3. 在 tauri::Builder 中注册
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![add, greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
|
三步流程:加宏 → 写函数 → generate_handler 注册。返回 Result 时,Err 会在前端抛出异常,天然适配 try/catch。
4.2 前端调用 invoke
前端使用 @tauri-apps/api 包的 invoke 函数,传入 command 名称和参数对象:
TypeScript - src/App.tsx
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import { invoke } from '@tauri-apps/api/core'
import { useState } from 'react'
function App() {
const [message, setMessage] = useState('')
const handleGreet = async () => {
try {
const result = await invoke<string>(
'greet',
{ name: 'Tauri 学习者' }
)
setMessage(result)
} catch (err) {
console.error('调用失败:', err)
}
}
return <button onClick={handleGreet}>问候</button>
}
|
💡 小贴士:Tauri 2.x 的 API 路径变了
2.x 版本中,@tauri-apps/api 拆分成了子模块。核心 API 从 @tauri-apps/api/core 导入,而不是 1.x 的 @tauri-apps/api/tauri。迁移时注意替换路径。
五、事件系统:后端向前端推送消息
Command 是前端主动调用后端的请求-响应模式。但有些场景需要后端主动推送——比如文件下载进度、系统通知、长任务状态更新。这时候用事件系统(Event System)。
事件系统的工作方式类似广播电台:后端(电台)在某个频道(事件名)上发射信号,前端(收音机)调到这个频道就能收到消息。一个事件可以有多个监听器,也可以只发给特定窗口。
5.1 Rust 端发送事件
Rust - 后端发送事件
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
use tauri::{AppHandle, Manager};
#[tauri::command]
async fn long_task(app: AppHandle) -> Result<(), String> {
for i in 1..=100 {
// 模拟耗时操作
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
// 发送进度事件
app
.emit("task-progress", i)
.map_err(|e| e.to_string())?;
}
// 发送完成事件
app.emit("task-complete", "任务完成!")
.map_err(|e| e.to_string())?;
Ok(())
}
|
5.2 前端监听事件
TypeScript - 前端监听事件
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import { listen } from '@tauri-apps/api/event'
import { useEffect, useState } from 'react'
function ProgressBar() {
const [progress, setProgress] = useState(0)
useEffect(() => {
let unlisten: Promise<Fn> | null = null
const setupListener = async () => {
unlisten = await listen<number>(
'task-progress',
(event) => setProgress(event.payload)
)
}
setupListener()
return () => { unlisten?.() }
}, [])
return <div>进度: {progress}%</div>
}
|
注意 listen 返回一个取消监听的函数,组件卸载时一定要调用,否则会造成内存泄漏。这和前端的 addEventListener / removeEventListener 是一个道理。
六、插件生态与系统能力调用
Tauri 核心只提供最基础的窗口和 IPC 能力,文件系统、通知、对话框这些系统能力都以插件(Plugin)形式提供。这种设计保持了核心的轻量,也让你按需引入功能。
6.1 常用官方插件
| 插件 |
功能 |
典型场景 |
| @tauri-apps/plugin-fs |
文件系统操作 |
读写文件、目录遍历 |
| @tauri-apps/plugin-dialog |
原生对话框 |
文件选择、消息提示 |
| @tauri-apps/plugin-notification |
系统通知 |
桌面推送通知 |
| @tauri-apps/plugin-shell |
Shell 命令执行 |
调用外部程序 |
| @tauri-apps/plugin-http |
HTTP 请求 |
绕过 CORS 的网络请求 |
| @tauri-apps/plugin-store |
持久化存储 |
配置文件保存 |
| @tauri-apps/plugin-updater |
自动更新 |
应用版本升级 |
6.2 插件使用三步曲
以 dialog 插件为例,使用一个插件需要三步:Cargo 加依赖、Rust 注册、前端调用。
Bash - 安装 dialog 插件
|
1
2
3
4
5
6
|
# 1. 前端安装 npm 包
npm install @tauri-apps/plugin-dialog
# 2. Rust 添加依赖(在 src-tauri/Cargo.toml)
# 或在 src-tauri 目录下执行:
cd src-tauri && cargo add tauri-plugin-dialog
|
Rust - main.rs 中注册插件
|
1
2
3
4
5
6
7
|
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_dialog::init())
.invoke_handler(tauri::generate_handler![...])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
|
TypeScript - 前端打开文件对话框
|
1
2
3
4
5
6
7
8
9
10
11
12
|
import { open } from '@tauri-apps/plugin-dialog'
const selectFile = async () => {
const selected = await open({
multiple: false,
filters: [{
name: '文本文件',
extensions: ['txt', 'md']
}]
})
console.log('选中的文件:', selected)
}
|
⚠️ 注意:2.x 的权限系统(Capabilities)
Tauri 2.x 引入了 Capabilities 权限系统,默认禁止一切前端调用。安装插件后还需要在 src-tauri/capabilities/ 目录下的 JSON 文件中声明允许的权限,否则前端调用会被拒绝。这是 2.x 相比 1.x 最大的安全增强。
七、常见错误与三层练习
7.1 常见错误
错误1:command not found
Invoke Error: command 'greet' not found
原因:Rust 函数加了 #[tauri::command],但忘了在 generate_handler! 宏里注册。
修复:在 tauri::generate_handler![add, greet] 中添加函数名。
错误2:权限被拒绝(2.x 特有)
scope not allowed: `dialog:allow-open` not allowed by any capability
原因:Tauri 2.x 的 Capability 系统默认禁止所有操作。
修复:在 src-tauri/capabilities/default.json 的 permissions 数组中添加 "dialog:allow-open"。
错误3:Linux 编译缺少 webkit2gtk
Package 'webkit2gtk-4.1' not found
原因:Linux 系统没有安装 WebView 开发库。
修复:Ubuntu 执行 sudo apt install libwebkit2gtk-4.1-dev,其他发行版参考官方文档。
7.2 三层练习
初
入门级:制作一个桌面版 Markdown 预览器
目标:左侧编辑 Markdown,右侧实时预览渲染结果
步骤:
1. 用 create-tauri-app 创建 React + TS 项目
2. 前端安装 marked 库做 Markdown 解析
3. 用 textarea 做编辑器,监听 input 事件实时渲染
4. 用 plugin-fs 实现"打开文件"和"保存文件"功能
验收:能打开 .md 文件、编辑后保存、左右分栏实时预览
中
进阶级:系统通知 + 倒计时应用
目标:桌面倒计时器,时间到了弹系统通知
步骤:
1. 前端设计倒计时输入界面(时/分/秒)
2. 用 setInterval 实现前端倒计时(简单版)
3. 进阶:倒计时逻辑移到 Rust 端,用事件推送进度到前端
4. 时间到调用 plugin-notification 发送系统通知
挑战:窗口最小化后倒计时仍然准确(提示:用 Rust 端计时避免浏览器节流)
高
挑战级:简易图片压缩工具
目标:批量压缩图片并保存到指定目录
步骤:
1. 用 plugin-dialog 选择多个图片文件
2. Rust 端引入 image crate 做图片压缩和格式转换
3. 用 Command 触发压缩任务,用 Event 实时推送每张的进度
4. 前端展示进度条、成功/失败列表
加分项:支持拖拽文件到窗口、支持质量参数调节、输出目录选择
八、知识回顾与下一篇预告
🏷️ Tauri 2.x 架构
🏷️ WebView vs Chromium
🏷️ #[tauri::command]
🏷️ invoke 调用
🏷️ emit / listen 事件
🏷️ Plugin 插件系统
🏷️ Capabilities 权限
🏷️ 包体积优化
本篇从 Tauri 的架构设计讲起,对比了它和 Electron 的本质区别:用系统 WebView 替代打包的 Chromium,用 Rust 替代 Node.js,换来 10 倍以上的体积缩减和更低的内存占用。我们学习了 Command 系统实现前端调用 Rust、事件系统实现后端主动推送,以及插件生态的使用方式。
Tauri 的设计哲学是"安全优先、按需加载"——每一个系统能力都需要显式声明权限,这增加了一点配置成本,但换来了更好的安全性和更小的包体积。对于追求轻量和性能的桌面应用,Tauri 是当前最值得关注的选择。
📌 下一篇预告
第 32 篇:Docker 容器化入门——应用打包与部署
完成了前端、App、桌面端的学习后,我们进入运维层。下一篇开始讲 Docker 容器化技术:如何把应用打包成镜像、如何用 Docker Compose 编排多服务、如何让你的应用在任何环境下一键启动。
觉得有用就点个❤️在看,分享给更多一起学习的朋友
— 全栈开发学习系列 · 第 31 篇 —