字
字节笔记本
2026年7月20日
Tauri 开发实战:API 能力全景与 Mac 应用打包上架
API中转
¥120
Tauri 开发实战:API 能力全景与 Mac 应用打包上架
Tauri 是一个用 Rust 构建桌面应用的框架,相比 Electron,包体积更小、性能更好、安全性更高。本文梳理 Tauri 提供的核心 API、C++ 互操作、截图功能实现,以及 Mac 应用打包上架的完整流程。
Tauri 核心 API 一览
Tauri 通过 Rust 后端 + Web 前端的架构,提供了丰富的系统级 API:
窗口管理
rust
use tauri::WindowBuilder;
// 创建新窗口
let window = WindowBuilder::new(app, "label")
.title("My Window")
.inner_size(800.0, 600.0)
.build()?;
// 控制窗口
window.maximize();
window.minimize();
window.close();系统对话框
rust
use tauri::api::dialog;
// 打开文件选择
let file_path = dialog::FileDialogBuilder::new().pick_file();
// 消息提示
dialog::MessageDialogBuilder::new("提示", "操作成功!").show(|_| {});文件系统
rust
use std::fs;
let contents = fs::read_to_string("config.txt")?;
fs::write("data.txt", "content")?;HTTP 客户端
Tauri 的 HTTP 客户端通过 tauri::http 模块提供,支持在 Rust 后端发起网络请求:
rust
use tauri::http::{Request, Response};系统托盘
rust
use tauri::SystemTray;
use tauri::SystemTrayEvent;
use tauri::CustomMenuItem;
let tray = SystemTray::new()
.with_menu(menu)
.on_tray_event(|app, event| match event {
SystemTrayEvent::LeftClick { .. } => {
// 点击托盘图标
}
SystemTrayEvent::MenuItemClick { id, .. } => {
// 菜单项点击
}
_ => {}
});事件系统
前后端通信的核心机制:
rust
// 后端监听事件
app.listen("custom-event", |event| {
println!("收到事件: {:?}", event.payload());
});
// 后端发送事件
app.emit("frontend-event", payload)?;javascript
// 前端监听
import { listen } from '@tauri-apps/api/event';
const unlisten = await listen('frontend-event', (event) => {
console.log(event.payload);
});全局快捷键
rust
use tauri::GlobalShortcutManager;
app.global_shortcut_manager()
.register("CommandOrControl+Shift+S", || {
println!("快捷键触发");
})?;剪贴板
rust
use tauri::api::clipboard;
clipboard::write_text("复制的文本")?;
let text = clipboard::read_text()?;通过 FFI 调用 C++ 代码
Tauri 后端是 Rust,可以通过 FFI(Foreign Function Interface)调用 C++ 编写的库。这对于复用现有的 C++ 库非常有用。
步骤 1:编写 C++ 代码
cpp
// src-tauri/cpp/calculator.h
#ifdef __cplusplus
extern "C" {
#endif
int add(int a, int b);
double multiply(double a, double b);
const char* get_version();
#ifdef __cplusplus
}
#endifcpp
// src-tauri/cpp/calculator.cpp
#include "calculator.h"
#include <string>
int add(int a, int b) {
return a + b;
}
double multiply(double a, double b) {
return a * b;
}
const char* get_version() {
static std::string version = "1.0.0";
return version.c_str();
}步骤 2:配置 build.rs 编译 C++
rust
// src-tauri/build.rs
fn main() {
cc::Build::new()
.cpp(true)
.file("cpp/calculator.cpp")
.compile("calculator");
}步骤 3:Rust 端 FFI 绑定
rust
// src-tauri/src/lib.rs
use std::os::raw::c_char;
use std::ffi::CStr;
#[link(name = "calculator", kind = "static")]
extern "C" {
fn add(a: i32, b: i32) -> i32;
fn multiply(a: f64, b: f64) -> f64;
fn get_version() -> *const c_char;
}
// 包装为 Tauri Command,供前端调用
#[tauri::command]
pub fn cpp_add(a: i32, b: i32) -> i32 {
unsafe { add(a, b) }
}
#[tauri::command]
pub fn cpp_get_version() -> String {
unsafe {
CStr::from_ptr(get_version())
.to_string_lossy()
.into_owned()
}
}步骤 4:前端调用
javascript
import { invoke } from '@tauri-apps/api/tauri';
const result = await invoke('cpp_add', { a: 1, b: 2 });
console.log(result); // 3注意事项:
- C++ 函数必须用
extern "C"包裹,避免 C++ 的名称修饰(name mangling) - 字符串用
*const c_char,Rust 端用CStr::from_ptr转换 - 内存管理需要特别小心,避免内存泄漏或悬垂指针
- Cargo.toml 中需要添加
cc作为 build-dependency
屏幕截图实现
Tauri 可以通过 screenshots-rs 库实现屏幕截图功能。
安装依赖
toml
# Cargo.toml
[dependencies]
screenshots = "0.8"
image = "0.24"
base64 = "0.21"Rust 后端实现
rust
use screenshots::Screen;
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
#[derive(serde::Serialize)]
pub struct ScreenInfo {
pub id: i32,
pub name: String,
pub width: u32,
pub height: u32,
}
#[tauri::command]
pub async fn get_screens() -> Result<Vec<ScreenInfo>, String> {
let screens = Screen::all().map_err(|e| e.to_string())?;
Ok(screens.iter().map(|s| ScreenInfo {
id: s.display_info.id,
name: s.display_info.name.clone(),
width: s.display_info.width,
height: s.display_info.height,
}).collect())
}
#[tauri::command]
pub async fn capture_screen(screen_id: i32) -> Result<String, String> {
let screens = Screen::all().map_err(|e| e.to_string())?;
let screen = screens.iter()
.find(|s| s.display_info.id == screen_id)
.ok_or("Screen not found")?;
let image = screen.capture().map_err(|e| e.to_string())?;
let mut buffer = Vec::new();
image.save_to(&mut std::io::Cursor::new(&mut buffer), image::ImageFormat::Png)
.map_err(|e| e.to_string())?;
Ok(BASE64.encode(&buffer))
}前端调用
javascript
import { invoke } from '@tauri-apps/api/tauri';
// 获取屏幕列表
const screens = await invoke('get_screens');
// 截取主屏幕
const base64 = await invoke('capture_screen', { screenId: screens[0].id });
// 显示截图
const img = document.createElement('img');
img.src = `data:image/png;base64,${base64}`;
document.body.appendChild(img);Mac 应用打包与上架
1. 配置 tauri.conf.json
json
{
"package": {
"productName": "YourApp",
"version": "1.0.0"
},
"tauri": {
"bundle": {
"identifier": "com.yourcompany.yourapp",
"icon": ["icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png"],
"active": true,
"category": "public.app-category.productivity",
"macOS": {
"minimumSystemVersion": "10.13",
"signingIdentity": "Apple Distribution: Your Company (TEAMID)",
"providerShortName": "TEAMID",
"entitlements": "entitlements.plist"
}
}
}
}2. 配置权限文件
xml
<!-- entitlements.plist -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.screen-capture</key>
<true/>
</dict>
</plist>3. 构建与签名
bash
# 构建 Universal Binary(同时支持 Intel 和 Apple Silicon)
npm run tauri build -- --target universal-apple-darwin
# 验证签名
codesign --verify -vvvv ./src-tauri/target/release/bundle/macos/YourApp.app4. 应用公证(Notarization)
苹果要求所有应用必须经过公证才能在 macOS 上正常运行:
bash
xcrun altool --notarize-app \
--primary-bundle-id "com.yourcompany.yourapp" \
--username "your@email.com" \
--password "app-specific-password" \
--file "./src-tauri/target/release/bundle/macos/YourApp.app"公证密码需要在 Apple ID 账户中生成应用专用密码,不能用普通登录密码。
5. 上传到 App Store Connect
bash
# 使用 Transporter 应用或命令行上传
xcrun altool --upload-app \
--type macos \
--file "./src-tauri/target/release/bundle/macos/YourApp.pkg" \
--username "your@email.com" \
--password "app-specific-password"上架前检查清单
- 隐私政策 URL
- 所有权限请求都有使用说明
- 应用描述和关键词
- 各尺寸截图(根据 Apple 要求)
- 测试账号(如果应用需要登录)
- 确保没有使用私有 API
- 确保没有违反 App Store 审核指南
Tauri vs Electron 对比
| 维度 | Tauri | Electron |
|---|---|---|
| 后端语言 | Rust | Node.js |
| 打包体积 | 最小约 600KB | 通常 100MB+ |
| 内存占用 | 较低 | 较高 |
| 安全模型 | 默认沙箱 + 权限控制 | 完全系统访问 |
| 跨平台 | Windows / macOS / Linux | Windows / macOS / Linux |
| 学习曲线 | 需要 Rust 基础 | JavaScript 开发者友好 |
Tauri 适合对包体积和性能有要求的应用,Electron 则在生态成熟度和开发门槛上有优势,根据项目需求选择即可。
分享: