跳到主要内容

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 核心区别

这是面试最高频的问题,需要从架构、体积、性能、安全等多个维度进行对比:

维度TauriElectron
后端语言RustNode.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)、SpacedriveVS Code、Slack、Notion、Discord
移动端支持Tauri 2.0 支持 iOS/Android不支持
学习曲线需学习 Rust 基础纯 JavaScript 即可
社区成熟度快速成长中非常成熟
Electron 开发者注意

从 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 最大的突破是将支持范围从桌面扩展到移动端,实现真正的跨平台:

tauri.conf.json - 移动端配置
{
"bundle": {
"iOS": {
"minimumSystemVersion": "13.0"
},
"android": {
"minSdkVersion": 24
}
}
}

3.2 插件系统重构

Tauri 2.0 将许多核心功能拆分为独立插件,按需引入:

npm install @tauri-apps/plugin-fs @tauri-apps/plugin-dialog @tauri-apps/plugin-shell
src/main.ts - 注册插件
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:

src-tauri/capabilities/default.json
{
"identifier": "default",
"description": "默认权限配置",
"windows": ["main"],
"permissions": [
"core:default",
"fs:allow-read-text-file",
"fs:allow-write-text-file",
"dialog:allow-open",
"shell:allow-open"
]
}
Tauri 1.x vs 2.0 权限对比
  • 1.x:在 tauri.conf.jsonallowlist 中配置布尔开关
  • 2.0:在 capabilities/ 目录中定义细粒度权限,可按窗口、按插件、按操作授权

4. 前后端通信

Tauri 的前后端通信有两种核心机制:Command 命令系统Event 事件系统

4.1 Command 命令系统(invoke)

Command 是最常用的通信方式,前端通过 invoke 调用 Rust 后端定义的命令:

src-tauri/src/lib.rs - 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");
}
src/App.tsx - 前端调用命令
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)

事件系统用于双向异步通信,适合后端主动推送数据的场景:

src-tauri/src/lib.rs - Rust 发送事件
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();
}
});
}
src/Download.tsx - 前端监听事件
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(能力)机制:

src-tauri/capabilities/main-window.json
{
"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 策略:

src-tauri/tauri.conf.json
{
"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 create tauri-app@latest

创建时可以选择前端模板:

框架模板选项构建工具
Reactreact / react-tsVite
Vuevue / vue-tsVite
Sveltesvelte / svelte-tsVite
Solidsolid / solid-tsVite
Next.jsnext / next-tsNext.js
Vanillavanilla / vanilla-tsVite

6.2 @tauri-apps/api 包

@tauri-apps/api 提供了与 Tauri 后端交互的 TypeScript API:

src/utils/tauri-helpers.ts
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 协调开发流程:

package.json
{
"scripts": {
"dev": "vite",
"tauri": "tauri",
"tauri:dev": "tauri dev",
"tauri:build": "tauri build"
}
}
vite.config.ts
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-httpHTTP 客户端(绕过 CORS)
Store@tauri-apps/plugin-store持久化键值存储
Updater@tauri-apps/plugin-updater应用自动更新
Log@tauri-apps/plugin-log日志记录
OS@tauri-apps/plugin-os操作系统信息
src/file-manager.ts - 使用文件系统插件
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 插件:

src-tauri/src/plugins/crypto.rs - 自定义加密插件
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()
}
src-tauri/src/lib.rs - 注册自定义插件
mod plugins;

fn main() {
tauri::Builder::default()
.plugin(plugins::crypto::init())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
src/crypto.ts - 前端调用自定义插件
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 格式
macOS3-8MB150-200MB.dmg / .app
Windows1-5MB100-170MB.msi / .nsis
Linux2-6MB120-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 代码签名

src-tauri/tauri.conf.json - macOS 签名配置
{
"bundle": {
"macOS": {
"signingIdentity": "Developer ID Application: Your Name (TEAM_ID)",
"entitlements": "./Entitlements.plist"
}
}
}
src-tauri/tauri.conf.json - Windows 签名配置
{
"bundle": {
"windows": {
"certificateThumbprint": "YOUR_CERT_THUMBPRINT",
"timestampUrl": "http://timestamp.digicert.com"
}
}
}

8.3 自动更新

Tauri 通过 @tauri-apps/plugin-updater 插件支持应用自动更新:

src/updater.ts
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 端点:

更新服务器返回的 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 性能优势

指标TauriElectron说明
冷启动~0.5-1s~2-5s无需加载 Chromium
内存占用(空闲)~30-50MB~150-200MB系统 WebView 共享内存
内存占用(运行中)~60-120MB~200-500MB取决于应用复杂度
CPU 占用(空闲)~0-1%~1-5%无 Chromium 后台进程
安装包大小1-8MB100-200MB不捆绑浏览器引擎
后端计算性能Rust 原生性能Node.js 性能Rust 比 JS 快 10-100x

9.2 WebView 限制

由于使用系统原生 WebView 而非捆绑 Chromium,Tauri 面临一些限制:

WebView 平台差异

不同平台的 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 需要独立安装版)

应对策略:

src/utils/platform.ts - 平台特性检测
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 最本质的区别是什么?

答案

最本质的区别在于 渲染引擎的来源后端语言

维度TauriElectron
渲染引擎使用操作系统自带的 WebView捆绑完整的 Chromium 浏览器
后端RustNode.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 的三大核心变化:

  1. 移动端支持:新增 iOS 和 Android 平台支持,使 Tauri 从桌面框架升级为全平台框架。移动端同样使用系统 WebView(iOS 的 WKWebView、Android 的 WebView)

  2. 插件系统重构:核心功能拆分为独立插件(fs、dialog、shell 等),按需引入。降低了核心包体积,也让第三方插件开发更规范

  3. 权限系统升级(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 默认给予渲染进程较高权限(除非启用 contextIsolationnodeIntegration: false 等),安全配置需要开发者手动加固。

Q6: Tauri 如何支持自动更新?

答案

Tauri 通过 @tauri-apps/plugin-updater 实现自动更新:

工作流程:

  1. 配置更新端点 URL(返回版本信息的 JSON)
  2. 应用启动时检查更新
  3. 下载新版本安装包
  4. 验证签名确保完整性
  5. 安装并重启
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 启动快?

  1. Rust 编译为原生二进制,无需解释器/运行时启动开销
  2. 系统 WebView 已常驻内存(尤其是 macOS 的 WKWebView),无需像 Chromium 那样冷启动整个浏览器进程
  3. 前端资源嵌入二进制中,无需从磁盘加载多个文件

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,在开发体验上有哪些不同?

答案

维度TauriElectron
开发启动tauri dev(编译 Rust,首次较慢约 30s-2min)electron .(启动快,几秒)
热重载前端 HMR 正常;Rust 修改需重新编译前端 HMR 正常;Node.js 修改重启快
调试前端内置 WebView InspectorChrome 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 的增量编译(修改尽量限制在少数文件)

相关链接