首页 / Tauri 2 入门教程 / 集成 React

Tauri 2 入门教程

集成 React

本教程共 48 篇 · 第 17 篇 · 更新于 2026-08-09 · 约 7 分钟阅读

TauriReactVite前端集成

本节目标:学完能用官方脚手架生成一个 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.tsserver.port 必须完全一致。不一致时开发模式会连不上前端,报错往往不明显,排查起来很费时间。

在 React 组件里调用 Tauri API

集成机制讲完,剩下的就是「在 React 里怎么写那句 invoke」。答案和原生前端完全一样:从 @tauri-apps/api/core 引入 invoke。区别只是它现在写在 React 组件的 functionuseState/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.tsserver.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.jsondevUrl 对齐;在 React 组件里 import { invoke } from '@tauri-apps/api/core' 调用 Rust。其余所有「原生能力」,都只是换个文件、换个模块名去 import,底层机制始终不变。