插件开发
插件能够hook到 Tauri 生命周期,暴露依赖于 web view API 的 Rust 代码,使用 Rust、Kotlin 或 Swift 代码处理命令,以及更多。
Tauri 提供了一个带有 web view 功能的窗口系统,一种在 Rust 进程和 web view 之间发送消息的方式,以及一个事件系统以及几个增强开发体验的工具。根据设计,Tauri 核心不包含并非所有人需要的功能。相反,它提供了一种机制,可以将外部功能添加到名为插件的 Tauri 应用程序中。
一个 Tauri 插件由一个 Cargo crate 和一个可选的 NPM 包组成,该包为其命令和事件提供 API 绑定。此外,一个插件项目可以包括一个 Android 库项目和一个用于 iOS 的 Swift 包。您可以在移动插件开发指南中了解更多关于为 Android 和 iOS 开发插件的信息。
命名约定
Tauri 插件有一个前缀,后跟插件名称。插件名称在插件配置中指定,位于tauri.conf.json > plugins
下。
默认情况下,Tauri 为您的插件 crate 添加前缀tauri-plugin-
。这有助于您的插件被 Tauri 社区发现,并与 Tauri CLI 一起使用。当初始化一个新的插件项目时,您必须提供其名称。生成的 crate 名称将是tauri-plugin-{plugin-name}
,JavaScript NPM 包名称将是tauri-plugin-{plugin-name}-api
(尽管我们建议尽可能使用NPM scope)。Tauri NPM 包的命名约定是@scope-name/plugin-{plugin-name}
。
初始化插件项目
要引导一个新的插件项目,请运行plugin new
。如果您不需要 NPM 包,请使用--no-api
CLI 标志。如果您想使用 Android 和/或 iOS 支持初始化插件,请使用--android
和/或 --ios
标志。
安装后,您可以运行以下命令来创建一个插件项目
npx @tauri-apps/cli plugin new [name]
这将在目录tauri-plugin-[name]
中初始化插件,并且根据使用的 CLI 标志,生成的项目将如下所示
. tauri-plugin-[name]/├── src/ - Rust code│ ├── commands.rs - Defines the commands the webview can use| ├── desktop.rs - Desktop implementation| ├── error.rs - Default error type to use in returned results│ ├── lib.rs - Re-exports appropriate implementation, setup state...│ ├── mobile.rs - Mobile implementation│ └── models.rs - Shared structs├── permissions/ - This will host (generated) permission files for commands├── android - Android library├── ios - Swift package├── guest-js - Source code of the JavaScript API bindings├── dist-js - Transpiled assets from guest-js├── Cargo.toml - Cargo crate metadata└── package.json - NPM package metadata
如果您有一个现有的插件,并希望为其添加 Android 或 iOS 功能,您可以使用plugin android add
和plugin ios add
来引导移动库项目,并指导您完成所需的更改。
移动插件开发
插件可以运行用 Kotlin(或 Java)和 Swift 编写的本机移动代码。默认插件模板包含一个使用 Kotlin 的 Android 库项目和一个 Swift 包。它包含一个示例移动命令,展示了如何从 Rust 代码触发其执行。
在移动插件开发指南中阅读更多关于为移动设备开发插件的信息。
插件配置
在使用插件的 Tauri 应用程序中,插件配置在tauri.conf.json
中指定,其中plugin-name
是插件的名称
{ "build": { ... }, "tauri": { ... }, "plugins": { "plugin-name": { "timeout": 30 } }}
插件的配置在Builder
上设置,并在运行时解析。这是一个使用Config
struct 指定插件配置的示例
use tauri::plugin::{Builder, Runtime, TauriPlugin};use serde::Deserialize;
// Define the plugin config#[derive(Deserialize)]struct Config { timeout: usize,}
pub fn init<R: Runtime>() -> TauriPlugin<R, Config> { // Make the plugin config optional // by using `Builder::<R, Option<Config>>` instead Builder::<R, Config>::new("<plugin-name>") .setup(|app, api| { let timeout = api.config().timeout; Ok(()) }) .build()}
生命周期事件
插件可以 hook 到几个生命周期事件
- setup: 插件正在初始化
- on_navigation: Web view 正在尝试执行导航
- on_webview_ready: 正在创建新窗口
- on_event: 事件循环事件
- on_drop: 插件正在被解构
移动插件还有其他生命周期事件。
setup
- 何时:插件正在初始化
- 原因:注册移动插件,管理状态,运行后台任务
use tauri::{Manager, plugin::Builder};use std::{collections::HashMap, sync::Mutex, time::Duration};
struct DummyStore(Mutex<HashMap<String, String>>);
Builder::new("<plugin-name>") .setup(|app, api| { app.manage(DummyStore(Default::default()));
let app_ = app.clone(); std::thread::spawn(move || { loop { app_.emit("tick", ()); std::thread::sleep(Duration::from_secs(1)); } });
Ok(()) })
on_navigation
- 何时:Web view 正在尝试执行导航
- 原因:验证导航或跟踪 URL 更改
返回 false
将取消导航。
use tauri::plugin::Builder;
Builder::new("<plugin-name>") .on_navigation(|window, url| { println!("window {} is navigating to {}", window.label(), url); // Cancels the navigation if forbidden url.scheme() != "forbidden" })
on_webview_ready
- 何时:已创建新窗口
- 原因:为每个窗口执行初始化脚本
use tauri::plugin::Builder;
Builder::new("<plugin-name>") .on_webview_ready(|window| { window.listen("content-loaded", |event| { println!("webview content has been loaded"); }); })
on_event
- 何时:事件循环事件
- 原因:处理核心事件,例如窗口事件、菜单事件和应用程序退出请求
通过此生命周期 hook,您可以收到任何事件循环事件的通知。
use std::{collections::HashMap, fs::write, sync::Mutex};use tauri::{plugin::Builder, Manager, RunEvent};
struct DummyStore(Mutex<HashMap<String, String>>);
Builder::new("<plugin-name>") .setup(|app, _api| { app.manage(DummyStore(Default::default())); Ok(()) }) .on_event(|app, event| { match event { RunEvent::ExitRequested { api, .. } => { // user requested a window to be closed and there's no windows left
// we can prevent the app from exiting: api.prevent_exit(); } RunEvent::Exit => { // app is going to exit, you can cleanup here
let store = app.state::<DummyStore>(); write( app.path().app_local_data_dir().unwrap().join("store.json"), serde_json::to_string(&*store.0.lock().unwrap()).unwrap(), ) .unwrap(); } _ => {} } })
on_drop
- 何时:插件正在被解构
- 原因:在插件被销毁时执行代码
有关更多信息,请参阅 Drop
。
use tauri::plugin::Builder;
Builder::new("<plugin-name>") .on_drop(|app| { // plugin has been destroyed... })
暴露 Rust API
在项目desktop.rs
和mobile.rs
中定义的插件 API 作为与插件同名(以 Pascal 命名法)的 struct 导出给用户。当插件设置时,将创建此 struct 的实例并将其作为状态进行管理,以便用户可以使用 Manager 实例(例如 AppHandle
、App
或 Window
)通过插件中定义的扩展 trait 在任何时间点检索它。
例如,global-shortcut plugin
定义了一个 GlobalShortcut
struct,可以通过使用 GlobalShortcutExt
trait 的 global_shortcut
方法来读取它
use tauri_plugin_global_shortcut::GlobalShortcutExt;
tauri::Builder::default() .plugin(tauri_plugin_global_shortcut::init()) .setup(|app| { app.global_shortcut().register(...); Ok(()) })
添加命令
命令在commands.rs
文件中定义。它们是常规的 Tauri 应用程序命令。它们可以直接访问 AppHandle 和 Window 实例,访问状态,并以与应用程序命令相同的方式接收输入。阅读命令指南以获取有关 Tauri 命令的更多详细信息。
此命令展示了如何通过依赖注入访问 AppHandle
和 Window
实例,并接受两个输入参数(on_progress
和 url
)
use tauri::{command, ipc::Channel, AppHandle, Runtime, Window};
#[command]async fn upload<R: Runtime>(app: AppHandle<R>, window: Window<R>, on_progress: Channel, url: String) { // implement command logic here on_progress.send(100).unwrap();}
要将命令暴露给 webview,您必须 hook 到 lib.rs
中的 invoke_handler()
调用
Builder::new("<plugin-name>") .invoke_handler(tauri::generate_handler![commands::upload])
在 webview-src/index.ts
中定义一个绑定函数,以便插件用户可以轻松地在 JavaScript 中调用该命令
import { invoke, Channel } from '@tauri-apps/api/core'
export async function upload(url: string, onProgressHandler: (progress: number) => void): Promise<void> { const onProgress = new Channel<number>() onProgress.onmessage = onProgressHandler await invoke('plugin:<plugin-name>|upload', { url, onProgress })}
请务必在测试之前构建 TypeScript 代码。
命令权限
默认情况下,您的命令无法通过前端访问。如果您尝试执行其中一个命令,您将收到拒绝错误。要实际暴露命令,您还需要定义允许每个命令的权限。
权限文件
权限被定义为权限目录内的 JSON 或 TOML 文件。每个文件可以定义权限列表、权限集列表和插件的默认权限。
权限
权限描述了插件命令的特权。它可以允许或拒绝命令列表,并关联命令特定的和全局的作用域。
"$schema" = "schemas/schema.json"
[[permission]]identifier = "allow-start-server"description = "Enables the start_server command."commands.allow = ["start_server"]
[[permission]]identifier = "deny-start-server"description = "Denies the start_server command."commands.deny = ["start_server"]
作用域
作用域允许您的插件为单个命令定义更深层次的限制。每个权限可以定义一个作用域对象列表,这些对象定义了允许或拒绝的内容,可以是命令特定的,也可以是插件全局的。
让我们定义一个示例 struct,它将保存 shell 插件允许生成的二进制文件列表的作用域数据
#[derive(Debug, schemars::JsonSchema)]pub struct Entry { pub binary: String,}
命令作用域
您的插件使用者可以在其功能文件中为特定命令定义作用域(请参阅文档)。您可以使用 tauri::ipc::CommandScope
struct 读取命令特定的作用域
use tauri::ipc::CommandScope;use crate::scope::Entry;
async fn spawn<R: tauri::Runtime>(app: tauri::AppHandle<R>, command_scope: CommandScope<'_, Entry>) -> Result<()> { let allowed = command_scope.allows(); let denied = command_scope.denies(); todo!()}
全局作用域
当权限未定义任何要允许或拒绝的命令时,它被视为作用域权限,并且它应该仅为您的插件定义全局作用域
[[permission]]identifier = "allow-spawn-node"description = "This scope permits spawning the `node` binary."
[[permission.scope.allow]]binary = "node"
您可以使用 tauri::ipc::GlobalScope
struct 读取全局作用域
use tauri::ipc::GlobalScope;use crate::scope::Entry;
async fn spawn<R: tauri::Runtime>(app: tauri::AppHandle<R>, scope: GlobalScope<'_, Entry>) -> Result<()> { let allowed = scope.allows(); let denied = scope.denies(); todo!()}
模式
作用域条目需要 schemars
依赖项来生成 JSON 模式,以便插件使用者知道作用域的格式并在其 IDE 中具有自动完成功能。
要定义模式,首先将依赖项添加到您的 Cargo.toml 文件中
# we need to add schemars to both dependencies and build-dependencies because the scope.rs module is shared between the app code and build script[dependencies]schemars = "0.8"
[build-dependencies]schemars = "0.8"
在您的构建脚本中,添加以下代码
#[path = "src/scope.rs"]mod scope;
const COMMANDS: &[&str] = &[];
fn main() { tauri_plugin::Builder::new(COMMANDS) .global_scope_schema(schemars::schema_for!(scope::Entry)) .build();}
权限集
权限集是各个权限的组,可帮助用户以更高的抽象级别管理您的插件。例如,如果单个 API 使用多个命令,或者一组命令之间存在逻辑连接,则应定义一个包含它们的集合
"$schema" = "schemas/schema.json"[[set]]identifier = "allow-websocket"description = "Allows connecting and sending messages through a WebSocket"permissions = ["allow-connect", "allow-send"]
默认权限
默认权限是一个特殊的权限集,标识符为 default
。建议您默认启用必需的命令。例如,如果不允许 request
命令,则 http
插件是无用的
"$schema" = "schemas/schema.json"[default]description = "Allows making HTTP requests"permissions = ["allow-request"]
自动生成的权限
为每个命令定义权限的最简单方法是使用在插件的 build.rs
文件中定义的构建脚本中定义的自动生成选项。在 COMMANDS
const 中,以 snake_case 定义命令列表(应与命令函数名称匹配),Tauri 将自动生成 allow-$commandname
和 deny-$commandname
权限。
以下示例生成 allow-upload
和 deny-upload
权限
const COMMANDS: &[&str] = &["upload"];
fn main() { tauri_plugin::Builder::new(COMMANDS).build();}
有关更多信息,请参阅权限概述文档。
管理状态
插件可以像 Tauri 应用程序一样管理状态。阅读状态管理指南以获取更多信息。
© 2025 Tauri 贡献者。CC-BY / MIT