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 init

Tauri 初始化配置

? 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 build

3. 项目结构

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.jsonallowlist 中启用 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 的 fetch API 替代浏览器原生 fetch
  • 考虑将 API 请求代理到后端

6. 问题:无法使用某些 Node.js 模块

解决方案

  • Tauri 不支持所有 Node.js 模块,特别是依赖原生代码的模块
  • 寻找替代方案,使用 Tauri 提供的 API
  • 或者在 Rust 端实现相应功能

进一步学习资源

  1. 官方文档

  2. 学习资源

  3. 示例项目

  4. 社区资源

  5. 工具和插件

课后练习

练习 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 开发轻量级桌面应用的核心技能,能够构建高性能、安全、跨平台的桌面应用。

« 上一篇 Vue 3与Electron桌面应用开发 - 跨平台桌面应用解决方案 下一篇 » Vue 3与Capacitor移动端开发