方式一、使用Splashscreen掩盖应用启动的耗时,同时完成初始化任务
1. 新建Splashscreen页面
在根目录中新建一个 splashscreen.html和 src/splashscreen-main.tsx 用于在软件启动过程前显示加载页面
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tauri App - Splash</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/splashscreen-main.tsx"></script>
</body>
</html>
import React from 'react'
import ReactDOM from 'react-dom/client'
import Splashscreen from './Splashscreen'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<Splashscreen />
</React.StrictMode>
)
然后创建 src/Splashscreen.tsx和 src/Splashscreen.css
import "./Splashscreen.css";
function Splashscreen() {
return (
<div className="container">
<h1>Tauri used Splash!</h1>
<div className="row">
<h5>It was super effective!</h5>
</div>
</div>
);
}
export default Splashscreen;
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
font-weight: 400;
color: #0f0f0f;
background-color: #f6f6f6;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
@media (prefers-color-scheme: dark) {
:root {
color: #f6f6f6;
background-color: #2f2f2f;
}
}
body {
margin: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.container {
margin: 0;
padding: 20px;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
}
.row {
display: flex;
justify-content: center;
}
h1 {
text-align: center;
margin: 0 0 10px 0;
}
h5 {
margin: 0;
font-weight: 500;
color: #646cff;
}
2. 在tauri.conf.json中注册新窗口
修改 src-tauri/tauri.conf.json,注册新窗口
"windows": [
{
"title": "tauri-app",
"width": 800,
"height": 600,
"label": "main",
"visible": false,
"center": true
},
{
"label": "splashscreen",
"url": "splashscreen.html",
"width": 400,
"height": 200,
"resizable": false,
"maximizable": false,
"minimizable": false,
"closable": false,
"decorations": false,
"alwaysOnTop": true,
"center": true,
"visible": true
}
]
3. 添加tokio异步运行时依赖
cd src-tauri
cargo add tokio -F time
cd ..
4. 修改lib.rs,支持Splashscreen自动关闭
use std::sync::Mutex;
use tauri::async_runtime::spawn;
use tauri::{AppHandle, Manager, State};
use tokio::time::{sleep, Duration};
// 跟踪前后端初始化任务完成状态
struct SetupState {
frontend_task: bool,
backend_task: bool,
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
// 将共享状态注册到 Tauri 状态管理
.manage(Mutex::new(SetupState {
frontend_task: false,
backend_task: false,
}))
.invoke_handler(tauri::generate_handler![greet, set_complete])
.setup(|app| {
// 异步执行后端初始化任务,不阻塞窗口创建
spawn(setup(app.handle().clone()));
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
#[tauri::command]
fn greet(name: String) -> String {
format!("Hello {name} from Rust!")
}
// 前端/后端完成各自初始化后调用此命令标记完成
#[tauri::command]
async fn set_complete(
app: AppHandle,
state: State<'_, Mutex<SetupState>>,
task: String,
) -> Result<(), ()> {
let mut state_lock = state.lock().unwrap();
match task.as_str() {
"frontend" => state_lock.frontend_task = true,
"backend" => state_lock.backend_task = true,
_ => panic!("invalid task completed!"),
}
// 前后端均就绪时关闭启动画面并显示主窗口
if state_lock.backend_task && state_lock.frontend_task {
let splash_window = app.get_webview_window("splashscreen").unwrap();
let main_window = app.get_webview_window("main").unwrap();
splash_window.close().unwrap();
main_window.show().unwrap();
}
Ok(())
}
// 模拟耗时后端初始化任务,同时等待前端就绪
async fn setup(app: AppHandle) -> Result<(), ()> {
println!("Performing really heavy backend setup task...");
let start_time = tokio::time::Instant::now();
loop {
let elapsed = start_time.elapsed();
// 确保启动画面至少展示 1 秒,避免闪烁
if elapsed < Duration::from_secs(1) {
sleep(Duration::from_millis(100)).await;
continue;
}
{
let state = app.state::<Mutex<SetupState>>();
let state_lock = state.lock().unwrap();
if state_lock.frontend_task {
break;
}
}
sleep(Duration::from_millis(100)).await;
}
println!("Backend setup task completed!");
// 标记后端任务完成
set_complete(
app.clone(),
app.state::<Mutex<SetupState>>(),
"backend".to_string(),
)
.await?;
Ok(())
}
5. 修改App.tsx模拟耗时操作
import { useState, useEffect } from "react";
import reactLogo from "./assets/react.svg";
import { invoke } from "@tauri-apps/api/core";
import "./App.css";
function App() {
const [greetMsg, setGreetMsg] = useState("");
const [name, setName] = useState("");
useEffect(() => {
// 模拟耗时操作(忙等待)
const start = Date.now();
while (Date.now() - start < 3000) {
// 模拟耗时计算
}
requestAnimationFrame(() => {
invoke("set_complete", { task: "frontend" });
});
}, []);
async function greet() {
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
setGreetMsg(await invoke("greet", { name }));
}
return (
<main className="container">
<h1>Welcome to Tauri + React</h1>
<div className="row">
<a href="https://vitejs.dev" target="_blank">
<img src="/vite.svg" className="logo vite" alt="Vite logo" />
</a>
<a href="https://tauri.app" target="_blank">
<img src="/tauri.svg" className="logo tauri" alt="Tauri logo" />
</a>
<a href="https://reactjs.org" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<p>Click on the Tauri, Vite, and React logos to learn more.</p>
<form
className="row"
onSubmit={(e) => {
e.preventDefault();
greet();
}}
>
<input
id="greet-input"
onChange={(e) => setName(e.currentTarget.value)}
placeholder="Enter a name..."
/>
<button type="submit">Greet</button>
</form>
<p>{greetMsg}</p>
</main>
);
}
export default App;
方式二、白屏时等Webview加载完成后再显示
1. 添加权限
在 src-tauri/default.json中添加 permision权限
"core:window:allow-show",
"core:window:allow-set-focus"
2. 设置visible
在 src-tauri/tauri.conf.json中设置 visible
"windows": [
{
"title": "tauri-app",
"width": 800,
"height": 600,
"visible": false
}
]
3. 在 src/App.tsx中添加延迟加载
import { useState, useEffect } from "react";
import reactLogo from "./assets/react.svg";
import { invoke } from "@tauri-apps/api/core";
import "./App.css";
import { getCurrentWindow } from '@tauri-apps/api/window';
function App() {
const [greetMsg, setGreetMsg] = useState("");
const [name, setName] = useState("");
const [ready, setReady] = useState(false);
useEffect(() => {
// 你的数据初始化逻辑放这里
async function init() {
// 比如:加载配置、读取本地数据等
// await loadConfig();
// await fetchInitialData();
// 数据准备就绪
setReady(true);
}
init();
}, []);
useEffect(() => {
if (!ready) return;
// 内容已渲染到 DOM,显示窗口
const show = async () => {
// 先让 CSS 生效,解除 visibility: hidden
document.body.classList.add('loaded');
// 再显示窗口
const appWindow = getCurrentWindow();
await appWindow.show();
await appWindow.setFocus();
};
// 等待下一帧渲染完成
requestAnimationFrame(() => {
requestAnimationFrame(() => {
show();
});
});
}, [ready]);
if (!ready) {
return null; // 数据没好之前不渲染任何内容
}
async function greet() {
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
setGreetMsg(await invoke("greet", { name }));
}
return (
<main className="container">
<h1>Welcome to Tauri + React</h1>
<div className="row">
<a href="https://vite.dev" target="_blank">
<img src="/vite.svg" className="logo vite" alt="Vite logo" />
</a>
<a href="https://tauri.app" target="_blank">
<img src="/tauri.svg" className="logo tauri" alt="Tauri logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<p>Click on the Tauri, Vite, and React logos to learn more.</p>
<form
className="row"
onSubmit={(e) => {
e.preventDefault();
greet();
}}
>
<input
id="greet-input"
onChange={(e) => setName(e.currentTarget.value)}
placeholder="Enter a name..."
/>
<button type="submit">Greet</button>
</form>
<p>{greetMsg}</p>
</main>
);
}
export default App;
评论