Tauri 桌面开发
问题
Tauri 的架构原理是什么?与 Electron 有哪些核心区别?Tauri 2.0 带来了哪些变化?如何进行前后端通信和安全管理?
答案
1. Tauri 架构概述
Tauri 是一个使用 Rust 构建后端、系统原生 WebView 渲染前端的跨平台桌面应用框架。与 Electron 捆绑 Chromium 不同,Tauri 利用操作系统自带的 WebView(macOS 上的 WKWebView、Windows 上的 WebView2/Edge、Linux 上的 WebKitGTK),从根本上解决了应用体积和内存占用问题。
核心理念:
- 安全优先:默认最小权限,所有系统能力需显式授权
- 极致轻量:不捆绑浏览器引擎,最小产物约 600KB
- Rust 驱动:后端逻辑用 Rust 编写,兼顾性能与内存安全
- 前端无关:支持任意前端框架(React、Vue、Svelte、Solid 等)
Tauri 的核心卖点是 "Web 技术写 UI + Rust 写后端 + 系统 WebView 渲染"。这使得最终产物极小、启动极快、内存占用极低,同时具备 Rust 级别的安全性。
2. 与 Electron 核心区别
这是面试最高频的问题,需要从架构、体积、性能、安全等多个维度进行对比:
| 维度 | Tauri | Electron |
|---|---|---|
| 后端语言 | Rust | Node.js (JavaScript) |
| 渲染引擎 | 系统原生 WebView | 捆绑 Chromium |
| 最小包体积 | ~600KB - 3MB | ~150MB+ |
| 内存占用 | ~30-80MB | ~150-300MB+ |
| 启动速度 | ~0.5-1s | ~2-5s |
| 安全模型 | 默认最小权限,细粒度 allowlist | 默认全权限,需手动限制 |
| 进程架构 | 单进程 + WebView | 多进程(主进程 + 渲染进程) |
| Node.js 支持 | 不支持(Rust 后端替代) | 完整 Node.js 运行时 |
| 前端框架 | 任意(React/Vue/Svelte 等) | 任意(React/Vue/Svelte 等) |
| 跨平台一致性 | WebView 行为有平台差异 | Chromium 保证一致性 |
| npm 生态 | 仅前端可用 npm 包 | 前后端都可用 npm 包 |
| 代表应用 | Cody (Sourcegraph)、Spacedrive | VS Code、Slack、Notion、Discord |
| 移动端支持 | Tauri 2.0 支持 iOS/Android | 不支持 |
| 学习曲线 | 需学习 Rust 基础 | 纯 JavaScript 即可 |
| 社区成熟度 | 快速成长中 | 非常成熟 |
从 Electron 迁移到 Tauri,最大的变化不是前端(前端代码基本可复用),而是 后端逻辑需要从 Node.js 改为 Rust。如果团队没有 Rust 经验,学习成本不可忽视。不过 Tauri 的 Rust 后端 API 设计得比较简洁,常见操作(文件读写、HTTP 请求、数据库等)通过官方插件即可完成,不需要深入 Rust 高级特性。
为什么体积差异这么大?
Electron 需要将整个 Chromium 浏览器引擎打包进应用,这是体积的主要来源。而 Tauri 使用操作系统已有的 WebView 组件,只需打包 Rust 编译的二进制和前端静态资源。
3. Tauri 2.0 新特性
Tauri 2.0 是一次重大版本升级,核心变化包括:
3.1 移动端支持(iOS / Android)
Tauri 2.0 最大的突破是将支持范围从桌面扩展到移动端,实现真正的跨平台:
{
"bundle": {
"iOS": {
"minimumSystemVersion": "13.0"
},
"android": {
"minSdkVersion": 24
}
}
}
3.2 插件系统重构
Tauri 2.0 将许多核心功能拆分为独立插件,按需引入:
- npm
- Yarn
- pnpm
- Bun
npm install @tauri-apps/plugin-fs @tauri-apps/plugin-dialog @tauri-apps/plugin-shell
yarn add @tauri-apps/plugin-fs @tauri-apps/plugin-dialog @tauri-apps/plugin-shell
pnpm add @tauri-apps/plugin-fs @tauri-apps/plugin-dialog @tauri-apps/plugin-shell
bun add @tauri-apps/plugin-fs @tauri-apps/plugin-dialog @tauri-apps/plugin-shell
import { register } from '@tauri-apps/plugin-fs';
import { open } from '@tauri-apps/plugin-dialog';
// Tauri 2.0 的插件通过 Cargo.toml 和前端 npm 包双重注册
3.3 权限系统(ACL)
Tauri 2.0 引入了基于 ACL(Access Control List)的细粒度权限系统,取代了 1.x 的 allowlist:
{
"identifier": "default",
"description": "默认权限配置",
"windows": ["main"],
"permissions": [
"core:default",
"fs:allow-read-text-file",
"fs:allow-write-text-file",
"dialog:allow-open",
"shell:allow-open"
]
}
- 1.x:在
tauri.conf.json的allowlist中配置布尔开关 - 2.0:在
capabilities/目录中定义细粒度权限,可按窗口、按插件、按操作授权
4. 前后端通信
Tauri 的前后端通信有两种核心机制:Command 命令系统 和 Event 事件系统。
4.1 Command 命令系统(invoke)
Command 是最常用的通信方式,前端通过 invoke 调用 Rust 后端定义的命令:
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct User {
name: String,
age: u32,
}
// 定义一个 Tauri 命令
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! Welcome to Tauri.", name)
}
// 支持异步命令
#[tauri::command]
async fn fetch_user(id: u32) -> Result<User, String> {
// 模拟数据库查询
Ok(User {
name: "Alice".to_string(),
age: 25,
})
}
// 注册命令
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet, fetch_user])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
import { invoke } from '@tauri-apps/api/core';
interface User {
name: string;
age: number;
}
// 调用 Rust 命令
async function handleGreet(): Promise<void> {
const message = await invoke<string>('greet', { name: 'World' });
console.log(message); // "Hello, World! Welcome to Tauri."
}
// 带类型安全的调用
async function handleFetchUser(): Promise<void> {
try {
const user = await invoke<User>('fetch_user', { id: 1 });
console.log(user.name, user.age);
} catch (error) {
console.error('Failed to fetch user:', error);
}
}
invoke 支持泛型参数,可以指定返回值类型。结合 Rust 端的 serde 序列化,可以实现前后端类型一致。社区还有 tauri-specta 等工具,可以自动从 Rust 类型生成 TypeScript 类型定义。
4.2 Event 事件系统(emit / listen)
事件系统用于双向异步通信,适合后端主动推送数据的场景:
use tauri::Emitter;
#[tauri::command]
fn start_download(app: tauri::AppHandle) {
std::thread::spawn(move || {
for progress in 0..=100 {
std::thread::sleep(std::time::Duration::from_millis(50));
app.emit("download-progress", progress).unwrap();
}
});
}
import { listen } from '@tauri-apps/api/event';
import { invoke } from '@tauri-apps/api/core';
import { useEffect, useState } from 'react';
function DownloadProgress(): JSX.Element {
const [progress, setProgress] = useState<number>(0);
useEffect(() => {
// 监听后端发送的事件
const unlisten = listen<number>('download-progress', (event) => {
setProgress(event.payload);
});
// 清理监听器
return () => {
unlisten.then((fn) => fn());
};
}, []);
const handleStart = async (): Promise<void> => {
await invoke('start_download');
};
return (
<div>
<button onClick={handleStart}>开始下载</button>
<progress value={progress} max={100} />
<span>{progress}%</span>
</div>
);
}
两种通信机制对比:
| 维度 | Command(invoke) | Event(emit/listen) |
|---|---|---|
| 方向 | 前端 -> 后端(请求/响应) | 双向(前端/后端都可发送) |
| 模式 | 请求-响应(类 RPC) | 发布-订阅 |
| 返回值 | 有返回值(Promise) | 无返回值(单向推送) |
| 适用场景 | 数据查询、操作触发 | 进度通知、状态推送、全局广播 |
5. 安全模型
Tauri 的安全设计是其核心优势之一,采用 "默认拒绝,显式授权" 的最小权限原则。
5.1 细粒度权限系统
Tauri 2.0 的权限系统基于 Capabilities(能力)机制:
{
"identifier": "main-window-capability",
"description": "主窗口权限",
"windows": ["main"],
"permissions": [
"core:default",
"fs:allow-read-text-file",
"fs:deny-read-file",
{
"identifier": "fs:scope",
"allow": [
{ "path": "$APPDATA/**" },
{ "path": "$DOCUMENT/**" }
],
"deny": [
{ "path": "$HOME/.ssh/**" }
]
},
"dialog:allow-open",
"dialog:allow-save"
]
}
5.2 CSP(内容安全策略)
Tauri 默认启用严格的 CSP 策略:
{
"app": {
"security": {
"csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' asset: https://asset.localhost",
"dangerousDisableAssetCspModification": false
}
}
}
5.3 安全架构总览
- 永远不要将
dangerousDisableAssetCspModification设为true - 避免在生产环境使用通配符权限(如
fs:allow-all) - 限制文件系统访问的作用域,只授权必要的目录
- 不要在前端代码中硬编码敏感信息,通过 Rust 后端处理
6. 前端集成
Tauri 对前端技术栈是完全无关的,支持主流的所有框架和构建工具。
6.1 创建项目
- npm
- Yarn
- pnpm
- Bun
npm create tauri-app@latest
yarn create tauri-app
pnpm create tauri-app
bunx create-tauri-app
创建时可以选择前端模板:
| 框架 | 模板选项 | 构建工具 |
|---|---|---|
| React | react / react-ts | Vite |
| Vue | vue / vue-ts | Vite |
| Svelte | svelte / svelte-ts | Vite |
| Solid | solid / solid-ts | Vite |
| Next.js | next / next-ts | Next.js |
| Vanilla | vanilla / vanilla-ts | Vite |
6.2 @tauri-apps/api 包
@tauri-apps/api 提供了与 Tauri 后端交互的 TypeScript API:
import { invoke } from '@tauri-apps/api/core';
import { listen, emit } from '@tauri-apps/api/event';
import { getCurrentWindow } from '@tauri-apps/api/window';
// 窗口操作
async function toggleFullscreen(): Promise<void> {
const window = getCurrentWindow();
const isFullscreen = await window.isFullscreen();
await window.setFullscreen(!isFullscreen);
}
// 带类型的 invoke 封装
async function api<T>(command: string, args?: Record<string, unknown>): Promise<T> {
try {
return await invoke<T>(command, args);
} catch (error) {
console.error(`Command "${command}" failed:`, error);
throw error;
}
}
6.3 Vite 集成
Tauri 默认使用 Vite 作为前端构建工具,通过 @tauri-apps/cli 协调开发流程:
{
"scripts": {
"dev": "vite",
"tauri": "tauri",
"tauri:dev": "tauri dev",
"tauri:build": "tauri build"
}
}
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
clearScreen: false,
server: {
port: 1420,
strictPort: true,
},
envPrefix: ['VITE_', 'TAURI_'],
});
7. 插件系统
Tauri 2.0 采用模块化的插件架构,核心功能通过插件提供。
7.1 官方插件
| 插件 | npm 包 | 功能 |
|---|---|---|
| File System | @tauri-apps/plugin-fs | 文件/目录读写操作 |
| Dialog | @tauri-apps/plugin-dialog | 系统文件选择/保存对话框 |
| Shell | @tauri-apps/plugin-shell | 执行系统命令、打开 URL |
| Clipboard | @tauri-apps/plugin-clipboard-manager | 剪贴板读写 |
| Notification | @tauri-apps/plugin-notification | 系统通知 |
| HTTP | @tauri-apps/plugin-http | HTTP 客户端(绕过 CORS) |
| Store | @tauri-apps/plugin-store | 持久化键值存储 |
| Updater | @tauri-apps/plugin-updater | 应用自动更新 |
| Log | @tauri-apps/plugin-log | 日志记录 |
| OS | @tauri-apps/plugin-os | 操作系统信息 |
import { readTextFile, writeTextFile, BaseDirectory } from '@tauri-apps/plugin-fs';
import { open, save } from '@tauri-apps/plugin-dialog';
// 打开文件选择器并读取内容
async function openFile(): Promise<string | null> {
const filePath = await open({
multiple: false,
filters: [{ name: 'Text', extensions: ['txt', 'md', 'json'] }],
});
if (typeof filePath === 'string') {
return await readTextFile(filePath);
}
return null;
}
// 保存文件
async function saveFile(content: string): Promise<void> {
const filePath = await save({
filters: [{ name: 'Text', extensions: ['txt'] }],
});
if (filePath) {
await writeTextFile(filePath, content);
}
}
// 使用 BaseDirectory 访问应用目录
async function saveConfig(config: object): Promise<void> {
await writeTextFile('config.json', JSON.stringify(config, null, 2), {
baseDir: BaseDirectory.AppData,
});
}
7.2 自定义 Rust 插件开发
当官方插件无法满足需求时,可以开发自定义 Rust 插件:
use tauri::plugin::{Builder, TauriPlugin};
use tauri::{Runtime, Manager};
use sha2::{Sha256, Digest};
#[tauri::command]
fn hash_sha256(input: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(input.as_bytes());
format!("{:x}", hasher.finalize())
}
// 构建插件
pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("crypto")
.invoke_handler(tauri::generate_handler![hash_sha256])
.build()
}
mod plugins;
fn main() {
tauri::Builder::default()
.plugin(plugins::crypto::init())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
import { invoke } from '@tauri-apps/api/core';
async function hashText(text: string): Promise<string> {
// 插件命令格式:plugin:<plugin-name>|<command-name>
return await invoke<string>('plugin:crypto|hash_sha256', { input: text });
}
8. 打包与分发
8.1 产物体积对比
| 平台 | Tauri 产物 | Electron 产物 | Tauri 格式 |
|---|---|---|---|
| macOS | 3-8MB | 150-200MB | .dmg / .app |
| Windows | 1-5MB | 100-170MB | .msi / .nsis |
| Linux | 2-6MB | 120-180MB | .deb / .AppImage |
| iOS | 取决于资源 | 不支持 | .ipa |
| Android | 取决于资源 | 不支持 | .apk / .aab |
Tauri 产物极小是因为不包含浏览器引擎。macOS 和 iOS 自带 WKWebView,Windows 10+ 自带 WebView2(基于 Edge),Linux 需要 WebKitGTK。Windows 上如果用户未安装 WebView2,Tauri 安装包会自动引导安装(约 1.5MB)。
8.2 代码签名
{
"bundle": {
"macOS": {
"signingIdentity": "Developer ID Application: Your Name (TEAM_ID)",
"entitlements": "./Entitlements.plist"
}
}
}
{
"bundle": {
"windows": {
"certificateThumbprint": "YOUR_CERT_THUMBPRINT",
"timestampUrl": "http://timestamp.digicert.com"
}
}
}
8.3 自动更新
Tauri 通过 @tauri-apps/plugin-updater 插件支持应用自动更新:
import { check } from '@tauri-apps/plugin-updater';
import { relaunch } from '@tauri-apps/plugin-process';
async function checkForUpdate(): Promise<void> {
const update = await check();
if (update) {
console.log(`发现新版本: ${update.version}`);
// 下载并安装更新
await update.downloadAndInstall((event) => {
switch (event.event) {
case 'Started':
console.log(`开始下载,总大小: ${event.data.contentLength}`);
break;
case 'Progress':
console.log(`已下载: ${event.data.chunkLength}`);
break;
case 'Finished':
console.log('下载完成');
break;
}
});
// 重启应用
await relaunch();
}
}
更新配置需要一个 JSON 端点:
{
"version": "1.2.0",
"notes": "Bug fixes and improvements",
"pub_date": "2026-03-01T00:00:00Z",
"platforms": {
"darwin-aarch64": {
"signature": "签名内容...",
"url": "https://releases.example.com/app-1.2.0-aarch64.app.tar.gz"
},
"windows-x86_64": {
"signature": "签名内容...",
"url": "https://releases.example.com/app-1.2.0-x64-setup.nsis.zip"
}
}
}
9. 性能特点与限制
9.1 性能优势
| 指标 | Tauri | Electron | 说明 |
|---|---|---|---|
| 冷启动 | ~0.5-1s | ~2-5s | 无需加载 Chromium |
| 内存占用(空闲) | ~30-50MB | ~150-200MB | 系统 WebView 共享内存 |
| 内存占用(运行中) | ~60-120MB | ~200-500MB | 取决于应用复杂度 |
| CPU 占用(空闲) | ~0-1% | ~1-5% | 无 Chromium 后台进程 |
| 安装包大小 | 1-8MB | 100-200MB | 不捆绑浏览器引擎 |
| 后端计算性能 | Rust 原生性能 | Node.js 性能 | Rust 比 JS 快 10-100x |
9.2 WebView 限制
由于使用系统原生 WebView 而非捆绑 Chromium,Tauri 面临一些限制:
不同平台的 WebView 引擎不同,可能导致渲染和 API 行为差异:
- macOS (WKWebView/WebKit): 可能不支持某些最新的 Chrome 特性
- Windows (WebView2/Edge): 基于 Chromium,兼容性最好
- Linux (WebKitGTK): 版本取决于用户系统,可能较旧
相比之下,Electron 捆绑固定版本的 Chromium,行为 100% 一致。
常见的兼容性问题:
- 某些 CSS 特性在 WebKit 和 Chromium 之间表现不同(如
backdrop-filter、CSS Nesting) - Web API 支持程度不同(如某些 Codec 支持、WebGPU)
- Linux 上 WebKitGTK 版本较旧时,可能缺少现代 CSS/JS 特性
- 无法使用 Chrome DevTools Extensions(如 React DevTools 需要独立安装版)
应对策略:
import { platform } from '@tauri-apps/plugin-os';
async function checkPlatformFeatures(): Promise<void> {
const os = await platform();
// 针对不同平台做兼容处理
if (os === 'linux') {
// Linux WebKitGTK 可能不支持某些特性,使用降级方案
console.log('Linux: 使用 fallback 渲染方案');
}
}
// CSS 特性检测
function supportsBackdropFilter(): boolean {
return CSS.supports('backdrop-filter', 'blur(10px)');
}
10. 适用场景与选型建议
选择 Tauri 的场景
- 体积敏感:需要小安装包的工具类应用(如 CLI 工具的 GUI 版本)
- 安全要求高:处理敏感数据的应用(如密码管理器、加密工具)
- 计算密集型:需要高性能后端计算(如文件处理、图像转换、数据分析)
- 多端覆盖:需要同时支持桌面和移动端(Tauri 2.0)
- 资源受限环境:用户设备配置较低,内存有限
选择 Electron 的场景
- 跨平台一致性:需要像素级一致的 UI 表现(如设计工具)
- Node.js 生态依赖:需要大量 npm 包(如使用 sharp、puppeteer 等原生模块)
- 团队技术栈:团队只熟悉 JavaScript/TypeScript,无 Rust 经验
- 复杂桌面功能:需要多窗口、系统托盘、深度系统集成等成熟方案
- 已有成熟产品:已有 Electron 应用,迁移成本不划算
Tauri 适合追求 轻量、安全、高性能 的新项目,尤其是工具类和移动端也需要的场景。Electron 适合需要 成熟生态、跨平台一致性、纯 JS 技术栈 的项目。两者不是非此即彼的关系,应根据团队和业务场景综合判断。
常见面试问题
Q1: Tauri 和 Electron 最本质的区别是什么?
答案:
最本质的区别在于 渲染引擎的来源 和 后端语言:
| 维度 | Tauri | Electron |
|---|---|---|
| 渲染引擎 | 使用操作系统自带的 WebView | 捆绑完整的 Chromium 浏览器 |
| 后端 | Rust | Node.js |
这个本质区别直接决定了两者在体积、内存、安全性上的差异:
- 体积:Tauri 不打包浏览器引擎,产物约 1-8MB;Electron 打包 Chromium,产物 150MB+
- 内存:Tauri 利用系统共享 WebView 内存;Electron 每个应用独立占用 Chromium 内存
- 安全:Tauri 的 Rust 后端天然避免内存安全问题,且默认最小权限;Electron 的 Node.js 后端需要手动限制权限
但也意味着 Tauri 不能保证跨平台 UI 一致性(不同系统 WebView 不同),而 Electron 可以。
Q2: Tauri 的前后端通信机制是怎样的?有哪些方式?
答案:
Tauri 提供两种通信机制:
1. Command 系统(invoke) - 请求/响应模式:
// 前端调用后端命令,类似 RPC
const result = await invoke<string>('greet', { name: 'Tauri' });
2. Event 系统(emit/listen) - 发布/订阅模式:
// 后端可以主动推送事件给前端
await listen<number>('progress', (event) => {
console.log(event.payload);
});
// 前端也可以发送事件给后端
await emit('user-action', { type: 'click' });
选择原则:
- 需要返回值的同步操作用
invoke(如读文件、查询数据) - 后端主动推送数据用
event(如下载进度、实时通知) - 前端间通信用
event(如多窗口数据同步)
Q3: Tauri 2.0 相比 1.x 有哪些重要变化?
答案:
Tauri 2.0 的三大核心变化:
-
移动端支持:新增 iOS 和 Android 平台支持,使 Tauri 从桌面框架升级为全平台框架。移动端同样使用系统 WebView(iOS 的 WKWebView、Android 的 WebView)
-
插件系统重构:核心功能拆分为独立插件(fs、dialog、shell 等),按需引入。降低了核心包体积,也让第三方插件开发更规范
-
权限系统升级(ACL):从 1.x 的简单
allowlist布尔开关,升级为基于 Capabilities 的细粒度 ACL 权限系统。支持按窗口、按插件、按操作、按路径作用域授权
Q4: 使用系统 WebView 而非 Chromium,有什么风险和局限?
答案:
主要风险:
- 平台不一致性:macOS 用 WebKit、Windows 用 Chromium 内核 Edge、Linux 用 WebKitGTK,CSS 和 JS API 支持程度不同
- 版本不可控:WebView 版本取决于用户操作系统,旧系统上可能缺少现代特性
- 调试困难:无法使用 Chrome DevTools Extensions(如 React/Vue DevTools),需要独立安装版
- Linux 兼容性:WebKitGTK 版本差异大,某些功能可能在旧发行版上不可用
应对措施:
- 使用 PostCSS/Autoprefixer 处理 CSS 兼容性
- 对关键 Web API 做特性检测和降级
- 充分测试所有目标平台
- 设置最低系统版本要求
Q5: Tauri 的安全模型是怎样的?为什么说比 Electron 更安全?
答案:
Tauri 的安全模型基于三个核心原则:
1. 最小权限(默认拒绝):
- 前端无法直接访问文件系统、网络、进程等系统能力
- 每个能力都需要在
capabilities/中显式授权 - 权限可以精确到具体操作(如只允许读文本文件,不允许读二进制)
2. 路径作用域限制:
{
"identifier": "fs:scope",
"allow": [{ "path": "$APPDATA/**" }],
"deny": [{ "path": "$HOME/.ssh/**" }]
}
即使授权了文件系统访问,也可以限制只能访问特定目录。
3. Rust 内存安全: Rust 的所有权系统在编译期杜绝了内存安全问题(空指针、缓冲区溢出、数据竞争),而 Electron 的 Node.js 原生模块可能存在这些风险。
与 Electron 对比:Electron 默认给予渲染进程较高权限(除非启用 contextIsolation、nodeIntegration: false 等),安全配置需要开发者手动加固。
Q6: Tauri 如何支持自动更新?
答案:
Tauri 通过 @tauri-apps/plugin-updater 实现自动更新:
工作流程:
- 配置更新端点 URL(返回版本信息的 JSON)
- 应用启动时检查更新
- 下载新版本安装包
- 验证签名确保完整性
- 安装并重启
import { check } from '@tauri-apps/plugin-updater';
import { relaunch } from '@tauri-apps/plugin-process';
const update = await check();
if (update) {
await update.downloadAndInstall();
await relaunch();
}
关键特性:
- 签名验证:所有更新包都经过签名验证,防止中间人攻击
- 增量更新:支持差量更新(delta updates),减少下载体积
- 自定义端点:可以使用任何静态文件服务器作为更新服务器
- 多平台支持:不同平台可以分别配置更新源
Q7: Tauri 中如何处理跨域(CORS)问题?
答案:
Tauri 处理 CORS 有独特的优势,因为 HTTP 请求可以通过 Rust 后端发出,完全绕过浏览器的同源策略:
方式一:通过 Rust Command 代理请求
// 前端
const data = await invoke<ApiResponse>('fetch_api', {
url: 'https://api.example.com/data'
});
// Rust 后端,不受 CORS 限制
#[tauri::command]
async fn fetch_api(url: &str) -> Result<String, String> {
reqwest::get(url).await
.map_err(|e| e.to_string())?
.text().await
.map_err(|e| e.to_string())
}
方式二:使用 HTTP 插件
@tauri-apps/plugin-http 提供的 fetch 函数通过 Rust 发送请求,不受 CORS 限制:
import { fetch } from '@tauri-apps/plugin-http';
const response = await fetch('https://api.example.com/data', {
method: 'GET',
});
这是 Tauri 相比纯 Web 应用的一个天然优势。
Q8: Tauri 的插件系统是怎样的?如何开发自定义插件?
答案:
Tauri 2.0 的插件系统由两部分组成:
1. Rust 端:实现核心逻辑
use tauri::plugin::{Builder, TauriPlugin};
use tauri::Runtime;
#[tauri::command]
fn my_command(input: &str) -> String {
format!("Processed: {}", input)
}
pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("my-plugin")
.invoke_handler(tauri::generate_handler![my_command])
.build()
}
2. 前端 TypeScript 包:提供类型安全的 API 封装
import { invoke } from '@tauri-apps/api/core';
export async function myCommand(input: string): Promise<string> {
return invoke<string>('plugin:my-plugin|my_command', { input });
}
插件生命周期钩子:
setup:插件初始化时调用on_page_load:页面加载时调用on_event:监听系统事件on_drop:插件销毁时调用
Q9: 什么场景下应该选 Tauri 而不是 Electron?
答案:
选 Tauri 的场景:
| 场景 | 原因 |
|---|---|
| 工具类应用(编辑器、转换器) | 体积小,安装快,启动快 |
| 安全敏感应用(密码管理器、加密工具) | Rust 内存安全 + 最小权限模型 |
| 计算密集型(文件处理、数据分析) | Rust 后端性能远超 Node.js |
| 需要移动端(桌面 + iOS/Android) | Tauri 2.0 原生支持 |
| 面向 C 端的轻量工具 | 用户不愿下载 150MB+ 的安装包 |
不选 Tauri 的场景:
| 场景 | 原因 |
|---|---|
| 需要跨平台 UI 像素级一致 | 系统 WebView 有差异 |
| 重度依赖 Node.js npm 生态 | Tauri 后端是 Rust,无法使用 Node 原生模块 |
| 团队无 Rust 经验且不愿学习 | Rust 学习曲线较陡 |
| 已有成熟 Electron 应用 | 迁移成本高,后端需要重写 |
Q10: Tauri 应用的启动流程是怎样的?
答案:
为什么 Tauri 启动快?
- Rust 编译为原生二进制,无需解释器/运行时启动开销
- 系统 WebView 已常驻内存(尤其是 macOS 的 WKWebView),无需像 Chromium 那样冷启动整个浏览器进程
- 前端资源嵌入二进制中,无需从磁盘加载多个文件
Q11: Tauri 中如何实现多窗口通信?
答案:
Tauri 支持创建多个窗口,窗口间通信主要通过事件系统实现:
import { emit } from '@tauri-apps/api/event';
import { WebviewWindow } from '@tauri-apps/api/webviewWindow';
// 创建子窗口
const settingsWindow = new WebviewWindow('settings', {
url: '/settings',
title: '设置',
width: 600,
height: 400,
});
// 向指定窗口发送事件
async function sendToSettings(data: object): Promise<void> {
await emit('config-update', data);
}
import { listen } from '@tauri-apps/api/event';
// 监听来自其他窗口的事件
await listen<object>('config-update', (event) => {
console.log('收到配置更新:', event.payload);
});
也可以通过 Rust 后端作为中转,在 Command 中操作特定窗口:
#[tauri::command]
fn send_to_window(app: tauri::AppHandle, label: &str, data: &str) {
if let Some(window) = app.get_webview_window(label) {
window.emit("message", data).unwrap();
}
}
Q12: Tauri 相比 Electron,在开发体验上有哪些不同?
答案:
| 维度 | Tauri | Electron |
|---|---|---|
| 开发启动 | tauri dev(编译 Rust,首次较慢约 30s-2min) | electron .(启动快,几秒) |
| 热重载 | 前端 HMR 正常;Rust 修改需重新编译 | 前端 HMR 正常;Node.js 修改重启快 |
| 调试前端 | 内置 WebView Inspector | Chrome DevTools 完整支持 |
| 调试后端 | 需要 Rust 调试器(如 lldb) | Node.js 调试器,生态成熟 |
| DevTools 扩展 | 不支持 Chrome 扩展(需独立版 React DevTools) | 完整支持 Chrome DevTools 扩展 |
| 构建时间 | 首次编译慢(Rust),增量编译快 | 打包慢(因为要打包 Chromium) |
| 错误提示 | Rust 编译器提示精确但需 Rust 知识 | JavaScript 错误,前端开发者熟悉 |
Tauri 的主要开发体验痛点是 Rust 编译时间。首次编译可能需要 1-3 分钟,增量编译通常在 5-15 秒。建议:
- 前端开发时单独用
pnpm dev,无需启动 Rust - 只在需要调试 IPC 通信时使用
tauri dev - 善用 Rust 的增量编译(修改尽量限制在少数文件)