【一起學Rust | Tauri2.0框架】基于 Rust 與 Tauri 2.0 框架實現全局狀態管理

前言

在現代應用程序開發中,狀態管理是構建復雜且可維護應用的關鍵。隨著應用程序規模的增長,組件之間共享和同步狀態變得越來越具有挑戰性。如果處理不當,狀態管理可能會導致代碼混亂、難以調試,并最終影響應用程序的性能和可擴展性。

Tauri 2.0 作為一個基于 Rust 的跨平臺應用程序開發框架,為我們提供了一個強大的工具集來構建高性能、安全且易于維護的桌面應用程序。結合 Rust 語言的優勢,我們可以實現高效且可靠的全局狀態管理。

本文將深入探討如何在 Tauri 2.0 應用程序中實現全局狀態管理。我們將從基本概念開始,逐步介紹不同的狀態管理方法,并通過實際代碼示例演示如何在 Tauri 2.0 項目中應用這些方法。無論你是 Rust 和 Tauri 的新手,還是有經驗的開發者,相信都能從本文中獲得有價值的知識和實踐經驗。

文章目錄

  • 前言
  • 一、全局狀態管理概述
    • 1.1 全局狀態管理的挑戰
    • 1.2 全局狀態管理的重要性
  • 二、Rust 與 Tauri 2.0 中的狀態管理
    • 2.1 Rust 的所有權和借用機制
        • 2.2 Tauri 2.0 的架構
  • 三、Tauri 2.0 內置狀態管理
    • 3.1 使用 `tauri::State`
    • 3.2 狀態更新與事件
    • 3.3 使用異步互斥鎖 (async mutex)
    • 3.4 使用`Arc`
    • 3.5 使用 Manager Trait來訪問狀態
    • 3.6 修復`Mismatching Types`
  • 四、使用第三方狀態管理庫
    • 4.1 `Redux` 啟發的狀態管理:`Yewdux`
    • 4.2 其他狀態管理庫
  • 五、狀態管理的最佳實踐
  • 六、實戰案例:構建一個簡單的計數器應用
    • 6.1 項目設置
    • 6.2 后端代碼
    • 6.3 前端代碼
    • 6.4 運行應用
  • 總結

一、全局狀態管理概述

全局狀態管理是指在應用程序的多個組件之間共享和同步數據的一種機制。這些數據可以是用戶界面狀態、應用程序配置、用戶數據等。全局狀態管理的目標是確保應用程序中的所有組件都能訪問和更新相同的狀態,從而保持數據的一致性和應用程序的整體協調性。

1.1 全局狀態管理的挑戰

在大型應用程序中,全局狀態管理面臨著以下挑戰:

  1. 數據一致性: 確保所有組件都能訪問和更新相同的狀態,避免數據不一致導致的問題。
  2. 組件通信: 在不同的組件之間傳遞狀態更新,確保所有相關組件都能及時響應狀態變化。
  3. 性能優化: 避免不必要的狀態更新和渲染,提高應用程序的性能。
  4. 代碼可維護性: 保持狀態管理代碼的清晰和簡潔,便于理解和維護。
  5. 可擴展性: 隨著應用程序的增長,狀態管理方案能夠適應新的需求和變化。

1.2 全局狀態管理的重要性

良好的全局狀態管理可以帶來以下好處:

  1. 簡化組件開發: 組件無需關心狀態的來源和更新,只需專注于自身的渲染和邏輯。
  2. 提高代碼可維護性: 將狀態管理邏輯集中處理,減少代碼重復和冗余。
  3. 增強應用程序可預測性: 狀態變化可追蹤、可預測,便于調試和問題排查。
  4. 提升用戶體驗: 確保應用程序在不同組件之間保持一致的狀態,提供流暢的用戶體驗。

二、Rust 與 Tauri 2.0 中的狀態管理

Rust 語言的特性和 Tauri 2.0 框架的架構為我們提供了多種實現全局狀態管理的方式。

2.1 Rust 的所有權和借用機制

Rust 的所有權和借用機制是其內存安全和并發安全的基礎。在狀態管理中,我們可以利用這些機制來確保狀態數據在不同組件之間的安全共享和訪問。

  • 所有權(Ownership): Rust 中的每個值都有一個被稱為其所有者的變量。在任何給定時間,一個值只能有一個所有者。當所有者超出作用域時,該值將被丟棄。
  • 借用(Borrowing): 我們可以通過引用(&)來借用一個值,而無需獲取其所有權。引用可以是可變的(&mut)或不可變的(&)。
  • 生命周期(Lifetime): 生命周期是 Rust 編譯器用來確保引用始終有效的機制。
2.2 Tauri 2.0 的架構

Tauri 2.0 采用了一種基于 Web 技術(HTML、CSS、JavaScript)構建前端界面,并使用 Rust 編寫后端邏輯的架構。這種架構使得我們可以利用 Web 生態系統中豐富的狀態管理庫,同時也能利用 Rust 的性能和安全性優勢。

Tauri 2.0 提供了以下機制來實現前端和后端之間的通信和狀態共享:

  1. 命令(Commands): 前端可以通過調用 Tauri 提供的命令來與后端進行交互。命令可以接收參數并返回結果。
  2. 事件(Events): 后端可以向前端發送事件,前端可以監聽這些事件并做出響應。
  3. 狀態(State): Tauri 2.0 提供了一個內置的狀態管理機制,允許我們在后端管理全局狀態,并在前端訪問和更新這些狀態。

三、Tauri 2.0 內置狀態管理

Tauri 2.0 提供了一個簡單而強大的內置狀態管理機制,可以滿足大多數應用程序的需求。

3.1 使用 tauri::State

tauri::State 是 Tauri 2.0 中用于管理全局狀態的核心類型。它是一個泛型類型,可以存儲任何實現了 SendSync trait 的類型。

  1. 定義狀態類型:

    #[derive(Default)]
    struct AppState {counter: std::sync::Mutex<i32>,
    }
    

    這里我們定義了一個名為 AppState 的結構體,其中包含一個名為 counter 的字段。counter 的類型是 std::sync::Mutex<i32>,表示一個受互斥鎖保護的 32 位整數。使用互斥鎖可以確保多個線程安全地訪問和修改 counter 的值。

  2. 初始化狀態:

    fn main() {tauri::Builder::default().manage(AppState::default()).invoke_handler(tauri::generate_handler![increment_counter, get_counter]).run(tauri::generate_context!()).expect("error while running tauri application");
    }
    

    main 函數中,我們使用 tauri::Builder::manage 方法將 AppState 的一個實例注冊為全局狀態。

  3. 在命令中訪問狀態:

    #[tauri::command]fn increment_counter(state: tauri::State<AppState>) -> Result<(), String> {let mut counter = state.counter.lock().map_err(|e| e.to_string())?;*counter += 1;Ok(())}#[tauri::command]fn get_counter(state: tauri::State<AppState>) -> Result<i32, String> {let counter = state.counter.lock().map_err(|e| e.to_string())?;Ok(*counter)}
  1. 在異步命令中訪問狀態:
    #[tauri::command]async fn increment_counter(state: tauri::State<AppState>) -> Result<(), String> {let mut counter = state.counter.await;*counter += 1;Ok(())}#[tauri::command]async fn get_counter(state: tauri::State<AppState>) -> Result<i32, String> {let counter = state.counter.await;Ok(*counter)}

我們定義了兩個命令:increment_counterget_counter。這兩個命令都接收一個 tauri::State<AppState> 類型的參數,表示對全局狀態的引用。

  • increment_counter 命令獲取 counter 的互斥鎖,將其值加 1,然后釋放鎖。
  • get_counter 命令獲取 counter 的互斥鎖,讀取其值,然后釋放鎖并返回該值。
  1. 在前端訪問狀態:
   import { invoke } from '@tauri-apps/api/tauri';async function incrementCounter() {await invoke('increment_counter');updateCounter();}async function updateCounter() {const counter = await invoke('get_counter');document.getElementById('counter').textContent = counter;}// 在頁面加載時更新計數器updateCounter();

在前端,我們使用 @tauri-apps/api/tauri 提供的 invoke 函數來調用后端命令。

  • incrementCounter 函數調用 increment_counter 命令,然后在狀態更新后調用 updateCounter 函數。
  • updateCounter 函數調用 get_counter 命令獲取計數器的當前值,并將其顯示在頁面上。

3.2 狀態更新與事件

在上面的示例中,我們通過調用 get_counter 命令來獲取狀態的更新。這種方式在狀態更新不頻繁的情況下是可行的。但是,如果狀態更新非常頻繁,或者我們需要在狀態更新時立即通知前端,那么使用事件機制會更有效。

  1. 在后端發送事件:

    #[tauri::command]
    fn increment_counter(state: tauri::State<AppState>, window: tauri::Window) -> Result<(), String> {let mut counter = state.counter.lock().map_err(|e| e.to_string())?;*counter += 1;window.emit("counter-updated", *counter).map_err(|e| e.to_string())?;Ok(())
    }
    

    increment_counter 命令中,我們在更新 counter 的值后,使用 window.emit 方法向前端發送一個名為 "counter-updated" 的事件,并將 counter 的當前值作為事件的負載。

  2. 在前端監聽事件:

    import { invoke } from '@tauri-apps/api/tauri';
    import { listen } from '@tauri-apps/api/event';async function incrementCounter() {await invoke('increment_counter');
    }// 監聽 counter-updated 事件
    listen('counter-updated', (event) => {document.getElementById('counter').textContent = event.payload;
    });// 在頁面加載時更新計數器
    updateCounter();
    

    在前端,我們使用 @tauri-apps/api/event 提供的 listen 函數來監聽 "counter-updated" 事件。當事件發生時,事件處理函數會被調用,并將事件的負載(即 counter 的當前值)顯示在頁面上。

3.3 使用異步互斥鎖 (async mutex)

該功能必須tokio啟用sync特征。

引用Tokio的文檔,使用標準庫的互斥體而不是Tokio提供的異步互斥體通常是可以的:

與普遍看法相反,在異步代碼中使用標準庫中的普通互斥體是可以的,而且通常是首選的……異步互斥體的主要用例是提供對IO資源(如數據庫連接)的共享可變訪問。

你需要充分閱讀Tokio的文檔以了解兩者之間的權衡。需要使用異步互斥的一個原因是,如果您需要在await之間保持MutexGuard。

這種類型的作用類似于 std::sync::Mutex,有兩個主要區別:lock 是一個異步方法,因此不會阻塞,并且鎖保護被設計為跨 .await 點持有。

Tokio的Mutex在保證FIFO的基礎上運行。 這意味著任務調用鎖方法的順序就是它們獲取鎖的順序。

也就是說,你通常使用標準庫的mutex基本上就能實現你的需求,因為async mutex實現起來成本高,因此Tokio官方直接就推薦使用標準庫的mutex了。tokio文檔中推薦使用:

  1. Arc<Mutex<...>>定義狀態
  2. 生成一個task線程來與主線程通信

并且給出了樣例代碼

use tokio::sync::Mutex;
use std::sync::Arc;#[tokio::main]
async fn main() {let count = Arc::new(Mutex::new(0));for i in 0..5 {let my_count = Arc::clone(&count);tokio::spawn(async move {for j in 0..10 {let mut lock = my_count.lock().await;*lock += 1;println!("{} {} {}", i, j, lock);}});}loop {if *count.lock().await >= 50 {break;}}println!("Count hit 50.");
}

在這個例子中,有幾件事需要注意。

  1. 互斥體被包裹在Arc中,以允許在線程之間共享。
  2. 每個生成的任務都會獲得一個鎖,并在每次迭代時釋放它
  3. Mutex保護的數據的突變是通過取消引用所獲得的鎖來完成的。

Tokio的Mutex采用簡單的FIFO(先進先出)風格,所有鎖定調用都按照執行順序完成。這樣,互斥體在如何將鎖分配給內部數據方面是“公平的”和可預測的。每次迭代后都會釋放并重新獲取鎖,因此基本上,每個線程在遞增一次值后都會轉到行的后面。請注意,線程啟動之間的時間存在一些不可預測性,但一旦它們啟動,它們就會可預測地交替。最后,由于在任何給定時間只有一個有效的鎖,因此在改變內部值時不可能出現競爭條件。

請注意,與std::sync::Mutex相反,當持有MutexGuard的線程崩潰時,此實現不會破壞互斥量。 在這種情況下,互斥鎖將被解鎖。 如果panic被捕獲,這可能會使受互斥鎖保護的數據處于不一致的狀態。

3.4 使用Arc

在 Rust 中,常見用法是使用 Arc 在多個線程之間共享一個值的所有權(通常與 Mutex 配對使用,形式為 Arc<Mutex<T>>)。但是,你不需要對存儲在 State 中的內容使用 Arc,因為 Tauri 會為你完成這項工作。

如果 State 的生命周期要求阻止你將狀態移動到新線程中,你可以改為將 AppHandle 移動到線程中,然后檢索你的狀態,如下面“使用 Manager trait 訪問狀態”部分所示。AppHandle 特意設計成易于克隆,以用于此類用例。

3.5 使用 Manager Trait來訪問狀態

有時你可能需要在命令之外訪問狀態,例如在不同的線程中或在像 on_window_event 這樣的事件處理程序中。在這種情況下,你可以使用實現了 Manager 特征的類型(例如 AppHandle)的 state() 方法來獲取狀態:

use tauri::{Builder, GlobalWindowEvent, Manager};#[derive(Default)]
struct AppState {counter: u32,
}// In an event handler:
fn on_window_event(event: GlobalWindowEvent) {// Get a handle to the app so we can get the global state.let app_handle = event.window().app_handle();let state = app_handle.state::<Mutex<AppState>>();// Lock the mutex to mutably access the state.let mut state = state.lock().unwrap();state.counter += 1;
}fn main() {Builder::default().setup(|app| {app.manage(Mutex::new(AppState::default()));Ok(())}).on_window_event(on_window_event).run(tauri::generate_context!()).unwrap();
}

當你不能依賴命令注入時,此方法非常有用。例如,如果你需要將狀態移動到使用 AppHandle 更容易的線程中,或者你不在命令上下文中。

3.6 修復Mismatching Types

如果你為 State 參數使用了錯誤的類型,你將得到一個運行時 panic,而不是編譯時錯誤。

例如,如果你使用 State<'_, AppState> 而不是 State<'_, Mutex<AppState>>,則不會有任何狀態使用該類型進行管理。

如果你愿意,你可以用類型別名包裝你的狀態以防止這個錯誤:

use std::sync::Mutex;#[derive(Default)]
struct AppStateInner {counter: u32,
}type AppState = Mutex<AppStateInner>;

但是,請確保按原樣使用類型別名,而不是再次將其包裝在 Mutex 中,否則你將遇到同樣的問題。

四、使用第三方狀態管理庫

除了 Tauri 2.0 的內置狀態管理機制,我們還可以使用 Rust 生態系統中的第三方狀態管理庫來實現更復雜的狀態管理需求。

4.1 Redux 啟發的狀態管理:Yewdux

Yewdux 是一個受 Redux 啟發的 Rust 狀態管理庫,它提供了一種基于單向數據流的狀態管理模式。

  1. 安裝 Yewdux

    cargo add yewdux
    
  2. 定義狀態和 Reducer

    use yewdux::prelude::*;#[derive(Default, Clone, PartialEq, Eq, Store)]
    struct AppState {counter: i32,
    }#[derive(Clone, PartialEq, Eq)]
    enum Action {Increment,
    }impl Reducer<AppState> for Action {fn apply(&self, mut state: Rc<AppState>) -> Rc<AppState> {let state = Rc::make_mut(&mut state);match self {Action::Increment => state.counter += 1,}state.clone().into()}
    }
    
    • 我們定義了一個名為 AppState 的結構體,其中包含一個 counter 字段。
    • 我們定義了一個名為 Action 的枚舉,表示可以對狀態執行的操作。
    • 我們為 Action 實現了 Reducer<AppState> trait,定義了如何根據不同的 Action 來更新狀態。
  3. 在 Tauri 中使用 Yewdux

    use yewdux::prelude::*;#[tauri::command]
    fn increment_counter(dispatch: Dispatch<AppState>) -> Result<(), String> {dispatch.apply(Action::Increment);Ok(())
    }#[tauri::command]
    fn get_counter(dispatch: Dispatch<AppState>) -> Result<i32, String> {Ok(dispatch.get().counter)
    }fn main() {tauri::Builder::default().setup(|app| {let dispatch = Dispatch::<AppState>::new();app.manage(dispatch);Ok(())}).invoke_handler(tauri::generate_handler![increment_counter, get_counter]).run(tauri::generate_context!()).expect("error while running tauri application");
    }
    
    • main 函數中,我們使用 Dispatch::<AppState>::new() 創建一個 Dispatch 實例,并將其注冊為 Tauri 的全局狀態。
    • 在命令中,我們通過 Dispatch<AppState> 類型的參數來訪問和修改狀態。
    • increment_counter 命令使用 dispatch.apply(Action::Increment) 來觸發狀態更新。
    • get_counter 命令使用 dispatch.get().counter 來獲取狀態的當前值。
  4. 在前端使用 Yewdux

    與 Tauri 內置狀態管理類似,我們可以使用 invoke 函數來調用后端命令,并通過事件或輪詢來獲取狀態更新。

4.2 其他狀態管理庫

除了 Yewdux,Rust 生態系統中還有其他一些狀態管理庫可供選擇,例如:

  • Relm4 一個基于 Elm 架構的 GUI 庫,它內置了狀態管理機制。
  • Iced 一個跨平臺的 GUI 庫,它也提供了自己的狀態管理方案。

五、狀態管理的最佳實踐

在 Tauri 2.0 應用程序中實現全局狀態管理時,可以遵循以下最佳實踐:

  1. 選擇合適的狀態管理方案: 根據應用程序的復雜度和需求選擇合適的狀態管理方案。對于簡單的應用程序,Tauri 2.0 的內置狀態管理機制可能就足夠了。對于更復雜的應用程序,可以考慮使用第三方狀態管理庫。
  2. 保持狀態的單一數據源: 避免在多個地方維護相同的狀態,確保狀態的唯一性和一致性。
  3. 使用不可變數據: 盡可能使用不可變數據來表示狀態,避免意外的狀態修改。
  4. 最小化狀態更新: 僅在必要時更新狀態,避免不必要的狀態更新和渲染。
  5. 使用選擇器(Selectors): 如果狀態數據比較復雜,可以使用選擇器來從狀態中提取所需的數據,避免在組件中直接訪問原始狀態。
  6. 使用調試工具: 利用 Tauri 2.0 和狀態管理庫提供的調試工具來跟蹤狀態變化和調試問題。
  7. 編寫測試: 為狀態管理邏輯編寫單元測試和集成測試,確保狀態管理的正確性和穩定性。

六、實戰案例:構建一個簡單的計數器應用

為了更好地理解如何在 Tauri 2.0 應用程序中實現全局狀態管理,我們將構建一個簡單的計數器應用。

6.1 項目設置

  1. 創建新的 Tauri 項目:

    cargo tauri init
    

    按照提示輸入項目名稱、窗口標題等信息。

  2. 安裝 Tauri API:

    npm install @tauri-apps/api
    

6.2 后端代碼

  1. 定義狀態類型:

    // src-tauri/src/main.rs#[derive(Default)]
    struct AppState {counter: std::sync::Mutex<i32>,
    }
    
  2. 實現命令:

    // src-tauri/src/main.rs#[tauri::command]
    fn increment_counter(state: tauri::State<AppState>, window: tauri::Window) -> Result<(), String> {let mut counter = state.counter.lock().map_err(|e| e.to_string())?;*counter += 1;window.emit("counter-updated", *counter).map_err(|e| e.to_string())?;Ok(())
    }#[tauri::command]
    fn get_counter(state: tauri::State<AppState>) -> Result<i32, String> {let counter = state.counter.lock().map_err(|e| e.to_string())?;Ok(*counter)
    }
    
  3. 注冊狀態和命令:

    // src-tauri/src/main.rsfn main() {tauri::Builder::default().manage(AppState::default()).invoke_handler(tauri::generate_handler![increment_counter, get_counter]).run(tauri::generate_context!()).expect("error while running tauri application");
    }
    

6.3 前端代碼

  1. 創建 HTML 結構:

    <!-- src/index.html --><!DOCTYPE html>
    <html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Tauri Counter App</title></head><body><h1>Counter: <span id="counter">0</span></h1><button id="increment-button">Increment</button><script src="main.js"></script></body>
    </html>
    
  2. 編寫 JavaScript 代碼:

    // src/main.jsimport { invoke } from '@tauri-apps/api/tauri';
    import { listen } from '@tauri-apps/api/event';async function incrementCounter() {await invoke('increment_counter');
    }// 監聽 counter-updated 事件
    listen('counter-updated', (event) => {document.getElementById('counter').textContent = event.payload;
    });// 在頁面加載時更新計數器
    async function updateCounter() {const counter = await invoke('get_counter');document.getElementById('counter').textContent = counter;
    }
    updateCounter()// 綁定按鈕點擊事件
    document.getElementById('increment-button').addEventListener('click', incrementCounter);
    

6.4 運行應用

cargo tauri dev

現在,你應該可以看到一個簡單的計數器應用。點擊 “Increment” 按鈕,計數器的值會增加,并且界面會實時更新。

總結

全局狀態管理是構建復雜 Tauri 2.0 應用程序的關鍵。本文深入探討了 Tauri 2.0 中的全局狀態管理,介紹了 Tauri 2.0 的內置狀態管理機制以及如何使用第三方狀態管理庫。通過結合 Rust 語言的優勢和 Tauri 2.0 框架的功能,我們可以構建高性能、安全且易于維護的桌面應用程序。

希望本文能夠幫助你更好地理解 Tauri 2.0 中的全局狀態管理,并在你的項目中應用這些知識。如果你有任何問題或建議,歡迎留言討論。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/74228.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/74228.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/74228.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

百度SEO和必應SEO優化方法

如需SEO服務&#xff0c;可以搜索&#xff1a;深圳市信科網絡科技有限公司。 一、搜索引擎生態格局&#xff1a;流量入口的重新洗牌 2025 年&#xff0c;中國 PC 端搜索引擎市場正經歷戲劇性變革。StatCounter 數據顯示&#xff0c;必應憑借 Edge 瀏覽器的預裝優勢與 ChatGPT …

Redis 事件機制詳解

Redis 事件機制詳解 Redis 的事件機制是其高性能和高并發能力的關鍵之一&#xff0c;它采用Reactor 模型&#xff0c;基于文件事件驅動機制實現高效的 I/O 處理。Redis 的事件機制主要分為以下幾類&#xff1a; 文件事件&#xff08;File Event&#xff09; —— 處理網絡 I/…

【LangChain入門 3 Prompts組件】聊天提示詞模板 ChatPromptTemplate

文章目錄 一、 聊天信息提示詞模板1.1 使用關鍵字1.2 使用SystemMessage, HumanMessage, AIMessage來定義消息1.3 使用MessagesPlaceholder 在特定未知添加消息列表 二、關鍵類介紹2.1 ChatPromptTemplate 類2.1.1 from_messages()2.1.2 format_messages()2.1.3 format_prompt(…

Flutter TextFormField 完全手冊與設計最佳實踐

目錄 1. 引言 2. TextFormField 的基本用法 3. 主要屬性 4. 自定義 TextFormField 樣式 4.1 設置邊框樣式 4.2 設置輸入格式限制 4.3 多行輸入 5. 結論 相關推薦 1. 引言 在 Flutter 中&#xff0c;TextFormField 是 TextField 的擴展版本&#xff0c;專為表單輸入設計…

HC-05與HC-06藍牙配對零基礎教程 以及openmv識別及遠程傳輸項目的概述

這個是上一年的項目&#xff0c;之前弄得不怎么完整&#xff0c;只有一個openmv的&#xff0c;所以openmv自己去我主頁找&#xff0c;這篇主要講藍牙 這個是我在使用openmv連接單片機1然后單片機1與單片機2通過藍牙進行通信 最終實現的效果是&#xff1a;openmv識別到圖形和數…

【Docker系列一】Docker 簡介

&#x1f49d;&#x1f49d;&#x1f49d;歡迎來到我的博客&#xff0c;很高興能夠在這里和您見面&#xff01;希望您在這里可以感受到一份輕松愉快的氛圍&#xff0c;不僅可以獲得有趣的內容和知識&#xff0c;也可以暢所欲言、分享您的想法和見解。 推薦:kwan 的首頁,持續學…

Vue 入門到實戰 五

第5章 過渡與動畫 目錄 5.1 單元素/組件過渡 5.1.1 過渡class 5.1.2 CSS 過渡 5.1.3 CSS 動畫 5.1.4 同時使用過渡和動畫 5.1.5 JavaScript 鉤子方法 5.2 多元素/組件過渡 5.2.1 多元素過渡 5.2.2 多組件過渡 5.3 列表過渡 5.3.1 列表的普通過渡 5.3.2 列表的平滑…

Apache SeaTunnel腳本升級及參數調優實戰

最近作者針對實時數倉的Apache SeaTunnel同步鏈路&#xff0c;完成了雙引擎架構升級與全鏈路參數深度調優&#xff0c;希望本文能夠給大家有所啟發&#xff0c;歡迎批評指正&#xff01; Apache SeaTunnel 版本 &#xff1a;2.3.9 Doris版本&#xff1a;2.0.6 MySQL JDBC Conne…

C++ 時間操作:獲取有史以來的天數與文件計數器

C 時間操作&#xff1a;獲取有史以來的天數與文件計數器 在C中&#xff0c;時間操作是一個非常重要的功能&#xff0c;尤其是在需要處理日期、時間戳或定時任務時。本文將介紹如何利用C的時間操作功能&#xff0c;實現以下兩個目標&#xff1a; 獲取從Unix紀元時間&#xff0…

Python Bug修復案例分析:Python 中常見的 IndentationError 錯誤 bug 的修復

在 Python 編程的世界里&#xff0c;代碼的可讀性和規范性至關重要。Python 通過強制使用縮進來表示代碼塊的層次結構&#xff0c;這一獨特的設計理念使得代碼更加清晰易讀。然而&#xff0c;正是這種對縮進的嚴格要求&#xff0c;導致開發者在編寫代碼時&#xff0c;稍有不慎就…

【論文筆記】Transformer

Transformer 2017 年&#xff0c;谷歌團隊提出 Transformer 結構&#xff0c;Transformer 首先應用在自然語言處理領域中的機器翻譯任務上&#xff0c;Transformer 結構完全構建于注意力機制&#xff0c;完全丟棄遞歸和卷積的結構&#xff0c;這使得 Transformer 結構效率更高…

CI/CD(三) 安裝nfs并指定k8s默認storageClass

一、NFS 服務端安裝&#xff08;主節點 10.60.0.20&#xff09; 1. 安裝 NFS 服務端 sudo apt update sudo apt install -y nfs-kernel-server 2. 創建共享目錄并配置權限 sudo mkdir -p /data/k8s sudo chown nobody:nogroup /data/k8s # 允許匿名訪問 sudo chmod 777 /dat…

【QA】單件模式在Qt中有哪些應用?

單例設計模式確保一個類僅有一個實例&#xff0c;并提供一個全局訪問點來獲取該實例。在 Qt 框架中&#xff0c;有不少類的設計采用了單例模式&#xff0c;以下為你詳細介紹并給出相應代碼示例。 1. QApplication QApplication 是 Qt GUI 應用程序的核心類&#xff0c;每個 Q…

存儲過程觸發器習題整理1

46、{blank}設有商品表(商品號&#xff0c;商品名&#xff0c;單價)和銷售表(銷售單據號&#xff0c;商品號&#xff0c;銷售時間&#xff0c;銷售數量&#xff0c;銷售單價)。其中&#xff0c;商品號代表一類商品&#xff0c;商品號、單價、銷售數量和銷售單價均為整型。請編寫…

基于ChatGPT、GIS與Python機器學習的地質災害風險評估、易發性分析、信息化建庫及災后重建高級實踐

第一章、ChatGPT、DeepSeek大語言模型提示詞與地質災害基礎及平臺介紹【基礎實踐篇】 1、什么是大模型&#xff1f; 大模型&#xff08;Large Language Model, LLM&#xff09;是一種基于深度學習技術的大規模自然語言處理模型。 代表性大模型&#xff1a;GPT-4、BERT、T5、Ch…

單表達式倒計時工具:datetime的極度優雅(智普清言)

一個簡單表達式&#xff0c;也可以優雅自成工具。 筆記模板由python腳本于2025-03-22 20:25:49創建&#xff0c;本篇筆記適合任意喜歡學習的coder翻閱。 【學習的細節是歡悅的歷程】 博客的核心價值&#xff1a;在于輸出思考與經驗&#xff0c;而不僅僅是知識的簡單復述。 Pyth…

最優編碼樹的雙子性

現在看一些書&#xff0c;不太愿意在書上面做一些標記&#xff0c;也沒啥特殊的原因。。哈哈。 樹的定義 無環連通圖&#xff0c;極小連通圖&#xff0c;極大無環圖。 度 某個節點&#xff0c;描述它的度&#xff0c;一般默認是出度&#xff0c;分叉的邊的條數。或者說孩子…

MiB和MB

本文來自騰訊元寶 MiB 和 ?MB 有區別&#xff0c;盡管它們都用于表示數據存儲的單位&#xff0c;但它們的計算方式不同&#xff0c;分別基于不同的進制系統。 1. ?MiB&#xff08;Mebibyte&#xff09;? ?MiB 是基于二進制的單位&#xff0c;使用1024作為基數。1 MiB 102…

Labview和C#調用KNX API 相關東西

敘述:完全沒有聽說過KNX這個協議...................我這次項目中也是簡單的用了一下沒有過多的去研究 C#調用示例工程鏈接(labview調用示例在 DEBUG文件夾里面) 通過網盤分享的文件&#xff1a;KNX調用示例.zip 鏈接: https://pan.baidu.com/s/1NQUEYM11HID0M4ksetrTyg?pwd…

損失函數理解(二)——交叉熵損失

損失函數的目的是為了定量描述不同模型&#xff08;例如神經網絡模型和人腦模型&#xff09;的差異。 交叉熵&#xff0c;顧名思義&#xff0c;與熵有關&#xff0c;先把模型換成熵這么一個數值&#xff0c;然后用這個數值比較不同模型之間的差異。 為什么要做這一步轉換&…