啟動畫面
在本實驗室中,我們將在 Tauri 應用程式中實作基本的啟動畫面功能。這樣做非常簡單,啟動畫面實際上只是建立一個新視窗,在您的應用程式執行一些繁重的設定相關任務期間顯示一些內容,然後在設定完成後關閉它。
先決條件
步驟
-
在您開始開發任何專案之前,先建置並執行初始模板非常重要,只是為了驗證您的設定是否按預期工作。
# Make sure you're in the right directorycd splashscreen-lab# Install dependenciespnpm install# Build and run the apppnpm tauri dev -
新增視窗最簡單的方法是直接將它們新增到
tauri.conf.json
。您也可以在啟動時動態建立它們,但為了簡單起見,讓我們只註冊它們。請確保您有一個標籤為main
的視窗,它被建立為隱藏視窗,以及一個標籤為splashscreen
的視窗,它被建立為直接顯示。您可以將所有其他選項保留為預設值,或根據喜好調整它們。src-tauri/tauri.conf.json {"windows": [{"label": "main","visible": false},{"label": "splashscreen","url": "/splashscreen"}]} -
在您開始之前,您需要有一些內容可以顯示。您如何開發新頁面取決於您選擇的框架,大多數框架都有「路由器」的概念來處理頁面導航,這應該像在 Tauri 中一樣正常工作,在這種情況下,您只需建立一個新的啟動畫面頁面。或者,正如我們將在這裡做的那樣,建立一個新的
splashscreen.html
檔案來託管內容。這裡重要的是您可以導航到
/splashscreen
URL 並顯示您想要的啟動畫面內容。在這個步驟之後嘗試再次執行應用程式!/splashscreen.html <!doctype html><html lang="en"><head><meta charset="UTF-8" /><link rel="stylesheet" href="/src/styles.css" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Tauri App</title></head><body><div class="container"><h1>Tauri used Splash!</h1><div class="row"><h5>It was super effective!</h5></div></div></body></html> -
由於啟動畫面通常旨在用於隱藏繁重的設定相關任務,讓我們假裝給應用程式一些繁重的工作要做,一些在前端,一些在後端。
為了在前端模擬繁重的設定,我們將使用一個簡單的
setTimeout
函數。在後端模擬繁重操作的最簡單方法是使用 Tokio crate,這是 Tauri 在後端使用的 Rust crate,以提供異步運行時。雖然 Tauri 提供了運行時,但 Tauri 沒有從中重新匯出各種實用程式,因此我們需要將 crate 新增到我們的專案中才能訪問它們。這在 Rust 生態系統中是一種完全正常的做法。
不要在異步函數中使用
std::thread::sleep
,它們在並發環境中協作運行,而不是並行運行,這意味著如果您睡眠線程而不是 Tokio 任務,您將鎖定排定在該線程上運行的所有任務,導致您的應用程式凍結。# Run this command where the `Cargo.toml` file iscd src-tauri# Add the Tokio cratecargo add tokio# Optionally go back to the top folder to keep developing# `tauri dev` can figure out where to run automaticallycd ..src/main.ts // These contents can be copy-pasted below the existing code, don't replace the entire file!!// Utility function to implement a sleep function in TypeScriptfunction sleep(seconds: number): Promise<void> {return new Promise(resolve => setTimeout(resolve, seconds * 1000));}// Setup functionasync function setup() {// Fake perform some really heavy setup taskconsole.log('Performing really heavy frontend setup task...')await sleep(3);console.log('Frontend setup task complete!')// Set the frontend task as being completedinvoke('set_complete', {task: 'frontend'})}// Effectively a JavaScript main functionwindow.addEventListener("DOMContentLoaded", () => {setup()});/src-tauri/src/lib.rs // Import functionalities we'll be usinguse std::sync::Mutex;use tauri::async_runtime::spawn;use tauri::{AppHandle, Manager, State};use tokio::time::{sleep, Duration};// Create a struct we'll use to track the completion of// setup related tasksstruct SetupState {frontend_task: bool,backend_task: bool,}// Our main entrypoint in a version 2 mobile compatible app#[cfg_attr(mobile, tauri::mobile_entry_point)]pub fn run() {// Don't write code before Tauri starts, write it in the// setup hook instead!tauri::Builder::default()// Register a `State` to be managed by Tauri// We need write access to it so we wrap it in a `Mutex`.manage(Mutex::new(SetupState {frontend_task: false,backend_task: false,}))// Add a command we can use to check.invoke_handler(tauri::generate_handler![greet, set_complete])// Use the setup hook to execute setup related tasks// Runs before the main loop, so no windows are yet created.setup(|app| {// Spawn setup as a non-blocking task so the windows can be// created and ran while it executesspawn(setup(app.handle().clone()));// The hook expects an Ok resultOk(())})// Run the app.run(tauri::generate_context!()).expect("error while running tauri application");}#[tauri::command]fn greet(name: String) -> String {format!("Hello {name} from Rust!")}// A custom task for setting the state of a setup task#[tauri::command]async fn set_complete(app: AppHandle,state: State<'_, Mutex<SetupState>>,task: String,) -> Result<(), ()> {// Lock the state without write accesslet 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!"),}// Check if both tasks are completedif state_lock.backend_task && state_lock.frontend_task {// Setup is complete, we can close the splashscreen// and unhide the main window!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(())}// An async function that does some heavy setup taskasync fn setup(app: AppHandle) -> Result<(), ()> {// Fake performing some heavy action for 3 secondsprintln!("Performing really heavy backend setup task...");sleep(Duration::from_secs(3)).await;println!("Backend setup task completed!");// Set the backend task as being completed// Commands can be ran as regular functions as long as you take// care of the input arguments yourselfset_complete(app.clone(),app.state::<Mutex<SetupState>>(),"backend".to_string(),).await?;Ok(())} -
執行應用程式
您現在應該會看到一個啟動畫面視窗彈出,前端和後端都將執行各自繁重的 3 秒設定任務,之後啟動畫面消失,主視窗顯示!
討論
您應該使用啟動畫面嗎?
一般來說,擁有啟動畫面是承認您無法讓您的應用程式載入速度快到不需要啟動畫面。事實上,最好直接進入主視窗,然後在角落的某個地方顯示一些小小的旋轉圖示,告知使用者後台仍在進行設定任務。
然而,話雖如此,擁有啟動畫面可能是一種風格選擇,或者您可能有一些非常特殊的要求,使得在執行某些任務之前無法啟動應用程式。擁有啟動畫面絕對不是錯誤的,它只是往往不是必要的,並且可能會讓使用者覺得應用程式沒有得到很好的優化。
© 2025 Tauri Contributors. CC-BY / MIT