Tauri 2.x插件生态与移动端实战

Tauri 2.0 的正式发布带来了两个大变化:完整的插件系统和移动端(iOS/Android)支持。用了一段时间后,记录下插件生态的现状和移动端踩过的坑。

Tauri 2.x 插件系统

Tauri 2.0 将原来内置的很多功能拆成了独立插件,比如文件系统访问、Shell 调用、HTTP 请求等。这是一个合理的设计——不是所有应用都需要所有能力,按需引入可以减小包体积,也更安全。

插件的引入方式是在 Rust 端和前端分别添加依赖:

# src-tauri/Cargo.toml
[dependencies]
tauri-plugin-store = "2"
tauri-plugin-shell = "2"
tauri-plugin-notification = "2"
tauri-plugin-http = "2"
# 前端
npm install @tauri-apps/plugin-store
npm install @tauri-apps/plugin-shell
npm install @tauri-apps/plugin-notification
npm install @tauri-apps/plugin-http

然后在 Tauri Builder 中注册:

// src-tauri/src/lib.rs
fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_store::Builder::new().build())
        .plugin(tauri_plugin_shell::init())
        .plugin(tauri_plugin_notification::init())
        .plugin(tauri_plugin_http::init())
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

常用插件速览

tauri-plugin-store:键值对持久化存储,替代 localStorage。数据存储在应用数据目录下的 JSON 文件中。

import { Store } from '@tauri-apps/plugin-store';

const store = await Store.load('settings.json');
await store.set('theme', 'dark');
await store.set('language', 'zh-CN');
await store.save(); // 显式保存到磁盘

const theme = await store.get<string>('theme');
console.log(theme); // "dark"

tauri-plugin-shell:执行系统命令。出于安全考虑,需要在配置中显式声明允许执行的命令。

// src-tauri/capabilities/default.json
{
  "permissions": [
    "shell:allow-execute",
    {
      "identifier": "shell:allow-spawn",
      "allow": [
        { "name": "git", "cmd": "git", "args": true }
      ]
    }
  ]
}
import { Command } from '@tauri-apps/plugin-shell';

const output = await Command.create('git', ['status']).execute();
console.log(output.stdout);

tauri-plugin-notification:系统通知。API 简洁:

import { sendNotification, isPermissionGranted, requestPermission } from '@tauri-apps/plugin-notification';

let permitted = await isPermissionGranted();
if (!permitted) {
    const permission = await requestPermission();
    permitted = permission === 'granted';
}
if (permitted) {
    sendNotification({ title: '下载完成', body: '文件已保存到 Downloads' });
}

tauri-plugin-http:HTTP 客户端。底层用 reqwest,比前端的 fetch 多了一些能力(比如跳过 CORS 限制、客户端证书)。

import { fetch } from '@tauri-apps/plugin-http';

const response = await fetch('https://api.example.com/data', {
    method: 'GET',
    headers: { 'Authorization': 'Bearer xxx' },
    connectTimeout: 5000,
});
const data = await response.json();

权限系统

Tauri 2.0 引入了 Capabilities 权限系统,取代了 1.x 的 allowlist。每个插件的每个操作都需要显式授权:

// src-tauri/capabilities/default.json
{
  "identifier": "main-capability",
  "windows": ["main"],
  "permissions": [
    "store:allow-get",
    "store:allow-set",
    "store:allow-save",
    "notification:default",
    "http:default"
  ]
}

这比 1.x 的粗粒度 allowlist 安全得多。可以为不同窗口配置不同的权限集,实现最小权限原则。

移动端适配

Tauri 2.0 支持编译到 iOS 和 Android。项目初始化时就可以添加移动端 target:

# 添加 Android 支持
cargo tauri android init

# 添加 iOS 支持
cargo tauri ios init

前端代码几乎不需要改动——这是 Tauri 的优势,UI 本来就是 WebView 渲染的。但 Rust 后端需要处理平台差异。

文件路径:各平台的数据目录不同。用 tauri::api::path 获取平台无关的路径:

use tauri::Manager;

#[tauri::command]
fn get_data_dir(app: tauri::AppHandle) -> Result<String, String> {
    app.path()
        .app_data_dir()
        .map(|p| p.to_string_lossy().to_string())
        .map_err(|e| e.to_string())
}

平台特定代码:通过条件编译处理:

#[tauri::command]
fn get_platform_info() -> String {
    #[cfg(target_os = "android")]
    { "Android".to_string() }

    #[cfg(target_os = "ios")]
    { "iOS".to_string() }

    #[cfg(desktop)]
    { format!("{}", std::env::consts::OS) }
}

移动端踩坑记录

WebView 兼容性:Android 的 WebView 版本取决于系统 WebView 组件的版本,不同设备差异很大。CSS 方面 backdrop-filter 在某些低版本 Android WebView 上不支持。建议加 fallback:

.blur-bg {
    background: rgba(255, 255, 255, 0.9); /* fallback */
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
}

状态栏和安全区域:移动端需要处理状态栏、刘海屏、底部安全区域。用 CSS 的 env(safe-area-inset-*) 来适配:

.app-container {
    padding-top: env(safe-area-inset-top);
    padding-bottom: env(safe-area-inset-bottom);
    padding-left: env(safe-area-inset-left);
    padding-right: env(safe-area-inset-right);
}

iOS 签名:需要有效的 Apple 开发者账号和证书。在 src-tauri/gen/apple 目录下配置 Bundle Identifier 和 Team ID。调试时可以用 Xcode 的自动签名。

Android 构建速度:首次编译 Android 会非常慢(需要编译 Rust 到 ARM 目标),后续增量编译会快很多。建议在开发阶段只编译一个 ABI(比如 arm64-v8a),发布时再编译全量。

# 只编译 arm64,加快开发速度
cargo tauri android dev --target aarch64

总结

Tauri 2.0 的插件生态已经覆盖了大多数常见需求,权限系统的设计也比较成熟。移动端支持虽然可用,但生态还在早期——部分插件对移动端的支持不完整,遇到问题需要翻源码。如果你的应用以桌面端为主、移动端为辅,Tauri 2.0 是一个不错的选择。