在 Tauri 中,插件是扩展应用能力的标准方式。除了使用社区提供的插件(如 fs、dialog、shell 等),当你有特定的需求无法被现有插件满足时,就可以开发自定义插件。这个过程比你想象的要简单——本质上就是把 Rust 功能封装成一个可复用的包,并暴露给前端。
1. 插件结构快览
一个典型的 Tauri 插件通常包含两个部分:
- Rust 端:一个标准的 Rust crate,实现具体功能、定义命令、声明权限。
- 前端端:一个 npm 包,提供 TypeScript 封装,让前端能舒适地调用插件命令,并带有类型提示。
在 Tauri v2 中,插件通过权限系统(Permissions)明确定义哪些命令可以被调用,这进一步强化了安全模型。
2. 快速上手:编写一个计算器插件
假设我们想封装一个安全的数学计算功能,让前端可以调用 add 和 divide 命令。
2.1 用脚手架创建插件项目
Tauri CLI 可以帮我们生成插件模板:
npm create tauri-plugin@latest
按提示选择插件名(如 tauri-plugin-calc)、是否包含前端 npm 包等。这会生成一个结构类似如下的目录:
tauri-plugin-calc/
├── Cargo.toml # Rust 依赖与元信息
├── src/
│ └── lib.rs # 插件逻辑
├── guest-js/
│ └── index.ts # 前端封装
├── permissions/ # 权限定义
│ └── default.toml
└── package.json # 前端包信息
2.2 编写 Rust 命令
在 lib.rs 中,我们定义两个命令并注册插件:
use tauri::plugin::{Builder, TauriPlugin};
#[tauri::command]
fn add(a: i32, b: i32) -> i32 {
a + b
}
#[tauri::command]
fn divide(a: i32, b: i32) -> Result<f64, String> {
if b == 0 {
Err("除数不能为零".into())
} else {
Ok(a as f64 / b as f64)
}
}
pub fn init() -> TauriPlugin<()> {
Builder::new("calc")
.invoke_handler(tauri::generate_handler![add, divide])
.build()
}
这里的 add 和 divide 就是可被前端调用的命令,divide 还展示了错误处理。
2.3 声明权限
在 permissions/default.toml 中,我们需要允许调用这两个命令:
# permissions/default.toml
[default]
description = "启用计算器插件的基础功能"
commands = ["add", "divide"]
scope = {}
若将来需要更细粒度的控制(比如只允许加法),可以定义额外的权限集。
2.4 编写前端封装
在 guest-js/index.ts 中,我们用 Tauri 的 invoke 包装命令,并暴露给用户:
import { invoke } from '@tauri-apps/api/core';
export async function add(a: number, b: number): Promise<number> {
return await invoke('plugin:calc|add', { a, b });
}
export async function divide(a: number, b: number): Promise<number> {
return await invoke('plugin:calc|divide', { a, b });
}
注意命令前缀 plugin:calc|,这是 Tauri v2 插件调用的格式,用于隔离不同插件的命名空间。
3. 在主应用中集成插件
使用这个插件只需要两步。
3.1 安装后端插件
在主 Tauri 项目的 src-tauri/Cargo.toml 中添加依赖:
[dependencies]
tauri-plugin-calc = { path = "../tauri-plugin-calc" } # 或发布后使用版本号
然后在 src-tauri/src/main.rs 中注册:
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_calc::init())
.run(tauri::generate_context!())
.expect("启动失败");
}
3.2 安装前端包
如果想把 TypeScript 封装也一起用,可以在主应用的项目中安装前端包(假设已发布到 npm 或本地 file:):
npm add tauri-plugin-calc
并在使用位置直接调用:
import { add } from 'tauri-plugin-calc';
const result = await add(1, 2);
3.3 启用权限
在主应用的 src-tauri/capabilities/ 目录下,为插件添加权限声明。如果插件提供了 default.toml,可以直接引用:
// src-tauri/capabilities/calc.json
{
"identifier": "calc-capability",
"windows": ["main"],
"permissions": [
"calc:default"
]
}
现在,前端调用 add 和 divide 就不会被权限系统拦截了。
4. 进阶技巧
- 传递状态:插件可以持有自己的结构化状态(数据库连接、设备句柄等),通过
Builder::setup(|app, _api| {...})在应用启动时初始化。 - 前端到后端的流式传输:使用
tauri::ipc::Channel可以让 Rust 命令返回一个流,前端通过onmessage监听实时数据。 - 条件编译:若插件功能只在特定平台有效,可以用
#[cfg(target_os = "windows")]限制。 - 分发与发布:将插件发布到 crates.io(Rust 端)和 npm(前端)后,其他开发者就能直接集成,享受类型安全和开箱即用的体验。
自定义插件把“一次编写,到处复用”的理念带到了 Tauri 生态中。当你发现自己需要在多个 Tauri 项目中重复相同的基础功能时,抽出 30 分钟将它做成一个插件,会是回报最高的决策之一。