Vue 3 与 Tauri 轻量级桌面应用
概述
Tauri 是一个现代化的跨平台桌面应用框架,使用 Rust 语言构建核心,利用系统原生 WebView 渲染 UI,相比 Electron 具有更小的体积和更高的性能。Vue 3 与 Tauri 的结合可以创建轻量级、高性能的桌面应用,同时享受 Vue 3 的现代化开发体验。本集将详细介绍如何使用 Vue 3 和 Tauri 构建桌面应用。
核心知识点
1. Tauri 基础架构
- Rust 核心:使用 Rust 语言编写的安全、高性能核心
- WebView 渲染:使用系统原生 WebView(Windows 上的 Edge WebView2、macOS 上的 WebKit、Linux 上的 WebKitGTK)
- IPC 通信:Rust 核心与 WebView 之间的安全通信机制
- 插件系统:可扩展的插件架构,支持访问原生功能
- 紧凑体积:应用体积通常在几 MB 级别,远小于 Electron 应用
2. Vue 3 + Tauri 项目初始化
安装 Tauri CLI
# 使用 npm 安装
npm install -g @tauri-apps/cli
# 使用 yarn 安装
yarn global add @tauri-apps/cli
# 使用 pnpm 安装
pnpm add -g @tauri-apps/cli创建 Vue 3 + Tauri 项目
# 使用 Vite 创建 Vue 3 项目
yarn create vite my-tauri-app -- --template vue
cd my-tauri-app
# 初始化 Tauri
yarn tauri initTauri 初始化配置
? What is your app name? my-tauri-app
? What should the window title be? My Tauri App
? Where are your web assets (HTML/CSS/JS) located? ../dist
? What is the url of your dev server? http://localhost:5173
? What is your frontend dev command? yarn dev
? What is your frontend build command? yarn build3. 项目结构
my-tauri-app/
├── src/ # Vue 3 应用源码
│ ├── assets/ # 静态资源
│ ├── components/ # Vue 组件
│ ├── App.vue # 根组件
│ └── main.js # 入口文件
├── public/ # 公共资源
├── src-tauri/ # Tauri 核心目录
│ ├── Cargo.toml # Rust 依赖配置
│ ├── build.rs # 构建脚本
│ ├── src/ # Rust 源码
│ │ ├── main.rs # Tauri 主入口
│ │ └── lib.rs # Rust 库
│ └── tauri.conf.json # Tauri 配置文件
├── index.html # HTML 入口
├── vite.config.js # Vite 配置
└── package.json # 前端依赖配置4. Tauri 配置文件
tauri.conf.json 基本配置
{
"$schema": "https://schema.tauri.app/config/1",
"build": {
"beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn build",
"devPath": "http://localhost:5173",
"distDir": "../dist"
},
"package": {
"productName": "My Tauri App",
"version": "1.0.0"
},
"tauri": {
"allowlist": {
"all": false,
"window": {
"all": true
},
"fs": {
"all": false,
"readFile": true,
"writeFile": true,
"readDir": true
}
},
"windows": [
{
"title": "My Tauri App",
"width": 800,
"height": 600,
"resizable": true,
"fullscreen": false
}
],
"security": {
"csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost;"
}
}
}5. Rust 主入口文件
main.rs 基本结构
// src-tauri/src/main.rs
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use tauri::Manager;
// 自定义命令
#[tauri::command]
async fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}
fn main() {
tauri::Builder::default()
// 注册自定义命令
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}6. Vue 3 应用与 Tauri 集成
在 Vue 组件中使用 Tauri API
<template>
<div class="container">
<h1>Vue 3 + Tauri App</h1>
<input v-model="name" placeholder="Enter your name" />
<button @click="greet">Greet</button>
<p>{{ greeting }}</p>
<button @click="openDialog">Open File Dialog</button>
<p v-if="selectedFile">Selected File: {{ selectedFile }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { invoke } from '@tauri-apps/api'
import { open } from '@tauri-apps/api/dialog'
import { readTextFile } from '@tauri-apps/api/fs'
const name = ref('')
const greeting = ref('')
const selectedFile = ref('')
// 调用自定义 Rust 命令
const greet = async () => {
greeting.value = await invoke('greet', { name: name.value })
}
// 使用 Tauri 文件对话框 API
const openDialog = async () => {
const filePath = await open({
multiple: false,
filters: [
{ name: 'Text Files', extensions: ['txt'] },
{ name: 'All Files', extensions: ['*'] }
]
})
if (filePath) {
selectedFile.value = filePath
// 读取文件内容
const content = await readTextFile(filePath)
console.log('File content:', content)
}
}
</script>
<style scoped>
.container {
text-align: center;
margin-top: 50px;
}
input, button {
margin: 10px;
padding: 8px 16px;
font-size: 14px;
}
button {
background-color: #42b883;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>7. Tauri API 分类
窗口 API
import {
minimize, maximize, unmaximize, close,
resize, setTitle, getCurrentWindow
} from '@tauri-apps/api/window'
const win = getCurrentWindow()
// 最小化窗口
await minimize()
// 最大化窗口
await maximize()
// 设置窗口标题
await setTitle('New Title')文件系统 API
import {
readTextFile, writeTextFile, exists,
createDir, removeDir, copyFile
} from '@tauri-apps/api/fs'
// 读取文件
const content = await readTextFile('path/to/file.txt')
// 写入文件
await writeTextFile('path/to/file.txt', 'Hello Tauri!')
// 检查文件是否存在
const fileExists = await exists('path/to/file.txt')对话框 API
import { open, save, message } from '@tauri-apps/api/dialog'
// 打开保存对话框
const filePath = await save({
filters: [{ name: 'JSON Files', extensions: ['json'] }]
})
// 显示消息对话框
await message('Hello from Tauri!', {
title: 'Message',
type: 'info'
})系统 API
import {
appVersion, appName, osVersion,
arch, tempdir, homeDir
} from '@tauri-apps/api/app'
// 获取应用版本
const version = await appVersion()
// 获取操作系统版本
const os = await osVersion()
// 获取临时目录
const temp = await tempdir()8. 自定义 Rust 命令
在 Rust 中定义命令
// src-tauri/src/main.rs
#[tauri::command]
async fn add(a: i32, b: i32) -> Result<i32, String> {
Ok(a + b)
}
#[tauri::command]
async fn read_file(path: String) -> Result<String, String> {
use std::fs::read_to_string;
read_to_string(path).map_err(|e| e.to_string())
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet, add, read_file])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}在 Vue 中调用自定义命令
// 调用加法命令
const result = await invoke('add', { a: 10, b: 20 })
console.log('Result:', result) // 输出: Result: 30
// 调用读取文件命令
const content = await invoke('read_file', { path: 'path/to/file.txt' })
console.log('File content:', content)9. 构建和打包
开发模式
# 启动开发服务器
yarn tauri dev构建生产版本
# 构建应用
yarn tauri build构建输出
target/
├── release/
│ ├── my-tauri-app.exe # Windows 可执行文件
│ ├── my-tauri-app.app # macOS 应用包
│ └── my-tauri-app # Linux 可执行文件
└── bundle/
├── dmg/ # macOS DMG 安装包
├── msi/ # Windows MSI 安装包
└── deb/ # Linux DEB 安装包最佳实践
1. 安全最佳实践
- 最小权限原则:只在
tauri.conf.json中启用必要的 API - 验证所有输入:对从前端传递到 Rust 的数据进行严格验证
- 使用安全的 CSP:配置合适的内容安全策略
- 避免暴露敏感信息:不在前端代码中存储敏感数据
- 定期更新依赖:保持 Tauri 和 Rust 依赖的最新版本
2. 性能优化
- 利用 Rust 处理密集计算:将 CPU 密集型任务放在 Rust 端执行
- 优化 WebView 渲染:使用 Vue 3 的性能优化特性(v-once, v-memo 等)
- 合理使用 IPC:减少不必要的 IPC 通信,合并相关请求
- 懒加载资源:延迟加载非必要的资源和组件
- 优化 Rust 代码:使用 Rust 的性能优化技巧
3. 开发工作流
- 使用 VS Code:安装 Tauri 和 Rust 扩展
- 热更新:利用 Vite 的热更新功能
- 调试:
- 前端:使用浏览器 DevTools
- Rust:使用 VS Code 的 Rust 调试器或 GDB
- 日志记录:使用
tauri::log宏记录 Rust 端日志
4. 跨平台兼容性
- 使用跨平台 API:优先使用 Tauri 提供的跨平台 API
- 处理平台差异:使用
std::env::consts::OS处理平台特定逻辑 - 测试不同平台:在 Windows、macOS 和 Linux 上测试
- 遵循平台设计规范:符合各平台的设计指南
5. 用户体验
- 启动速度优化:最小化启动时的资源加载
- 响应式设计:适配不同屏幕尺寸
- 原生外观:使用系统原生控件和主题
- 键盘快捷键:支持常用键盘快捷键
- 通知:使用系统通知功能
常见问题与解决方案
1. 问题:Tauri 开发服务器无法启动
解决方案:
- 确保已安装 Rust 环境(
rustc --version) - 检查端口是否被占用(默认 5173)
- 确保 Vite 开发服务器正常运行
- 检查
tauri.conf.json中的devPath配置
2. 问题:无法访问文件系统 API
解决方案:
- 在
tauri.conf.json的allowlist中启用fs相关权限 - 确保文件路径格式正确
- 检查应用是否有足够的权限访问目标文件
3. 问题:构建失败
解决方案:
- 确保已安装所有必要的构建依赖
- 检查 Rust 版本是否符合要求
- 清理构建缓存(
cargo clean) - 检查
Cargo.toml中的依赖配置
4. 问题:应用体积过大
解决方案:
- 优化 Rust 依赖,移除不必要的库
- 配置
Cargo.toml使用--release构建 - 启用链接时优化(LTO):在
Cargo.toml中添加lto = true - 优化前端资源,使用代码拆分和 Tree Shaking
5. 问题:跨域请求失败
解决方案:
- 在
tauri.conf.json中配置security.csp允许必要的源 - 使用 Tauri 的
fetchAPI 替代浏览器原生 fetch - 考虑将 API 请求代理到后端
6. 问题:无法使用某些 Node.js 模块
解决方案:
- Tauri 不支持所有 Node.js 模块,特别是依赖原生代码的模块
- 寻找替代方案,使用 Tauri 提供的 API
- 或者在 Rust 端实现相应功能
进一步学习资源
官方文档
学习资源
示例项目
社区资源
工具和插件
课后练习
练习 1:创建基础 Tauri 应用
- 使用 Vite 创建 Vue 3 项目
- 初始化 Tauri 配置
- 运行开发服务器
- 构建生产版本
练习 2:实现基本功能
- 在 Rust 中定义自定义命令
- 在 Vue 中调用自定义命令
- 实现一个简单的计数器功能
- 实现文件读取和写入功能
练习 3:使用 Tauri API
- 使用窗口 API 实现窗口控制(最小化、最大化、关闭)
- 使用对话框 API 实现文件选择和保存
- 使用通知 API 发送系统通知
- 使用剪贴板 API 实现复制粘贴功能
练习 4:构建复杂应用
- 创建一个带有多个页面的 Vue 3 应用
- 实现状态管理(使用 Pinia)
- 集成 Tauri 的文件系统 API
- 实现数据持久化
练习 5:性能优化
- 将密集计算任务迁移到 Rust 端
- 优化 IPC 通信
- 实现懒加载和代码拆分
- 配置 Rust 构建优化
练习 6:跨平台适配
- 在不同平台(Windows、macOS、Linux)上测试应用
- 处理平台特定的逻辑
- 实现符合平台设计规范的 UI
- 构建跨平台安装包
通过以上练习,你将掌握 Vue 3 与 Tauri 开发轻量级桌面应用的核心技能,能够构建高性能、安全、跨平台的桌面应用。