集成 React
本教程共 48 篇 · 第 17 篇 · 更新于 2026-08-09 · 约 7 分钟阅读
本节目标:学完能用官方脚手架生成一个 React 模板,理解开发端口为什么要「锁死」,并能在 React 组件里正确调用 Tauri 的
invoke。
React 是目前使用最广的声明式前端框架之一,靠「组件(component)」拼界面、用「状态(state)」驱动更新。Tauri 和 React 的集成,思路和前面几章完全一致:底层还是 WebView 加载页面、还是靠 invoke 调 Rust,变的只是前端代码怎么组织。本章只讲「集成机制」和「调用方式」,不带着做一个完整产品。先记住一句话:Tauri 对前端框架是「中立」的,它只在乎你最终产出的 HTML/CSS/JS。
用脚手架生成 React 模板
Tauri 官方的交互式脚手架 create-tauri-app 内置了 React 模板,能一次性把前端和 Rust 两端搭好。在空目录里运行:
npm create tauri-app@latest
命令会依次问你几件事:项目名、包管理器,以及最关键的前端模板选择。在模板列表里选 React(TypeScript) 或 React(JavaScript) 即可——带 TypeScript 的版本类型提示更友好,推荐新手选它。脚手架会自动完成三件事:创建 src/ 作为 React 前端、创建 src-tauri/ 作为 Rust 后端、并装好 @tauri-apps/api 与 @tauri-apps/cli 这两个包。
生成后目录大致如下:
my-react-app/
├── src/ # React 前端源码
│ ├── App.tsx # 根组件
│ ├── main.tsx # 入口,挂载 React 应用
│ └── vite.config.ts # Vite 配置
├── src-tauri/ # Rust 后端
│ ├── tauri.conf.json
│ └── src/lib.rs
└── package.json
你平时写界面都在 src/ 里,Rust 相关的事都在 src-tauri/ 里,两者通过配置和 IPC 连起来。启动开发用 npm run tauri dev,构建发布用 npm run tauri build。
关键配置:锁死 Vite 端口
React 模板默认用 Vite 做开发服务器。这里有一个集成时「必设点」:Tauri 在开发模式下要靠一个固定的网址(devUrl)找到你的前端,而 Vite 默认端口「占到哪个算哪个」,端口被占用时会自动跳到 5174、5175。一旦端口飘了,Tauri 就接不上了,窗口会白屏。
解决办法是在 src/vite.config.ts 里把端口固定,并开启 strictPort(端口被占用就直接报错,而不是悄悄换端口):
// src/vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
// 阻止 Vite 清屏,避免覆盖 Rust 编译日志
clearScreen: false,
server: {
port: 5173,
strictPort: true,
watch: {
// 告诉 Vite 不要监听 Rust 目录,避免无意义重载
ignored: ['**/src-tauri/**'],
},
},
});
对应的,tauri.conf.json 里的 build 配置要和这个端口对齐:
{
"build": {
"beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run build",
"devUrl": "http://localhost:5173",
"frontendDist": "../dist"
}
}
Tip
clearScreen: false是 React/Vite 模板的常见写法,它能防止 Vite 清屏,让 Rust 编译日志和 Vite 日志互不覆盖,调试时看得更清楚。它不影响功能,但建议保留。
Warning
devUrl的端口和vite.config.ts的server.port必须完全一致。不一致时开发模式会连不上前端,报错往往不明显,排查起来很费时间。
在 React 组件里调用 Tauri API
集成机制讲完,剩下的就是「在 React 里怎么写那句 invoke」。答案和原生前端完全一样:从 @tauri-apps/api/core 引入 invoke。区别只是它现在写在 React 组件的 function 或 useState/useEffect 逻辑里。
下面用一个最小例子演示:点按钮把输入框的名字发给 Rust,再把返回值显示出来。
// src/App.tsx
import { useState } from 'react';
import { invoke } from '@tauri-apps/api/core';
export default function App() {
const [name, setName] = useState('');
const [reply, setReply] = useState('');
async function greet() {
// 调用 Rust 端的 greet 命令,参数用对象传
const result = await invoke('greet', { name });
setReply(result as string);
}
return (
<main>
<h1>React + Tauri</h1>
<input value={name} onChange={(e) => setName(e.target.value)} placeholder="输入你的名字" />
<button onClick={greet}>打招呼</button>
<p>{reply}</p>
</main>
);
}
后端 src-tauri/src/lib.rs 里,greet 命令的定义和前面章节一致:
#[tauri::command]
fn greet(name: String) -> String {
format!("你好,{}!来自 Rust 的问候。", name)
}
invoke 返回的是 Promise,所以在 React 的 async 函数里用 await 取值,再交给 useState 管理的状态变量,界面就自动重渲染了。这就是 React 响应式 + Tauri 原生能力的标准配合方式。
Note别忘了第 14 章提过的安全模型:这个
greet命令需要在src-tauri/capabilities/的能力文件里被授权,前端才能调得通。脚手架生成的 React 模板已经把默认命令和core:default权限配好了,你新增命令时记得补上权限,否则会收到权限错误。
启动开发与构建发布
配置和调用都就绪后,日常命令就两条:开发用 npm run tauri dev,它会先按 beforeDevCommand 起 Vite、再编译 Rust 并弹出窗口;发布用 npm run tauri build,产物是带界面的安装包。如果开发模式窗口白屏,第一反应应该是检查 devUrl 端口和 vite.config.ts 的 server.port 是否一致——这是 React 模板最高频的坑。
不止 invoke:其它 API 模块
@tauri-apps/api 不止有 core。在 React 组件里,你同样可以按模块引入其它能力,比如操作当前窗口:
import { getCurrentWindow } from '@tauri-apps/api/window';
// 隐藏当前窗口
await getCurrentWindow().hide();
再比如收发事件(对应第 14 章的「事件」通道):
import { listen } from '@tauri-apps/api/event';
await listen('progress', (event) => {
console.log(event.payload);
});
这些 API 的引入方式和 invoke 一模一样,都是标准 ES 模块导入,在 .tsx 文件顶部 import 即可。需要注意:部分能力(如文件系统、进程、对话框)属于独立插件,除了 import,还要在 Rust 端 .plugin(...) 注册、并在能力文件里授权——这属于插件章节的内容,这里先知道「API 按模块引入」这个统一规律即可。
小结
React 与 Tauri 的集成可以浓缩成三句话:用 create-tauri-app 选 React 模板一键生成;在 vite.config.ts 里把端口 strictPort 锁死,并和 tauri.conf.json 的 devUrl 对齐;在 React 组件里 import { invoke } from '@tauri-apps/api/core' 调用 Rust。其余所有「原生能力」,都只是换个文件、换个模块名去 import,底层机制始终不变。