Zustand 状态管理
灵动桌面框架使用 Zustand 5(zustand ^5.0.11)作为全局状态管理方案,轻量、简洁、TypeScript 友好。
框架内置两个 Store:
| Store | 文件 | 状态 | 方法 |
|---|---|---|---|
useAppStore | src/store/app.ts | theme、sidebarCollapsed | toggleTheme、setTheme、toggleSidebar |
useSettingsStore | src/store/settings.ts | language、closeBehavior | setLanguage、setCloseBehavior |
基本用法
定义 Store
// src/store/app.ts
import { create } from "zustand";
export type ThemeMode = "light" | "dark" | "system";
/** 解析实际生效的主题(system → 读取系统偏好) */
export function resolveTheme(mode: ThemeMode): "light" | "dark" {
if (mode !== "system") return mode;
return window.matchMedia?.("(prefers-color-scheme: dark)").matches
? "dark"
: "light";
}
interface AppStore {
/** 主题模式(含跟随系统) */
theme: ThemeMode;
/** 侧边栏是否折叠 */
sidebarCollapsed: boolean;
/** 三态切换主题:dark → light → system → dark */
toggleTheme: () => void;
/** 设置主题 */
setTheme: (theme: ThemeMode) => void;
/** 切换侧边栏 */
toggleSidebar: () => void;
}
const TOGGLE_ORDER: ThemeMode[] = ["dark", "light", "system"];
export const useAppStore = create<AppStore>((set) => ({
theme: "system",
sidebarCollapsed: false,
toggleTheme: () =>
set((s) => {
const idx = TOGGLE_ORDER.indexOf(s.theme);
const next = TOGGLE_ORDER[(idx + 1) % TOGGLE_ORDER.length];
return { theme: next };
}),
setTheme: (theme) => set({ theme }),
toggleSidebar: () => set((s) => ({ sidebarCollapsed: !s.sidebarCollapsed })),
}));
在组件中使用
// 选择器订阅单个字段(src/App.tsx、src/components/layout/Sidebar.tsx)
const appTheme = useAppStore((s) => s.theme);
const collapsed = useAppStore((s) => s.sidebarCollapsed);
// 解构整个 Store(src/components/ui/ThemeToggle.tsx、AppLayout.tsx)
const { theme, toggleTheme } = useAppStore();
不传选择器时组件会订阅整个 Store,任一字段变化都会触发重渲染;ThemeToggle 和 AppLayout 只用到 useAppStore 里的少数字段,影响很小。
设置 Store
// src/store/settings.ts
import { create } from "zustand";
interface SettingsStore {
/** 语言 */
language: string;
/** 设置语言 */
setLanguage: (lang: string) => void;
/** 关闭行为:minimize(最小化到托盘)| exit(直接退出) */
closeBehavior: "minimize" | "exit";
/** 设置关闭行为 */
setCloseBehavior: (behavior: "minimize" | "exit") => void;
}
export const useSettingsStore = create<SettingsStore>((set) => ({
language: "zh-CN",
setLanguage: (language) => set({ language }),
closeBehavior: "minimize",
setCloseBehavior: (closeBehavior) => set({ closeBehavior }),
}));
内置 Store 不持久化
两个内置 Store 都只存在内存里:重启应用后 theme 回到 "system"、language 回到 "zh-CN"。目前也没有页面读写 useSettingsStore,它是留给业务扩展的骨架。
持久化(扩展示例)
Rust 端已注册 tauri-plugin-store(src-tauri/src/lib.rs),默认权限包含 store:default,前端依赖里也有 @tauri-apps/plugin-store。需要持久化时,可以在 Store 的方法里同步写入,下面是一个写法示例(框架未内置):
import { create } from "zustand";
import { Store } from "@tauri-apps/plugin-store";
interface SettingsStore {
language: string;
setLanguage: (lang: string) => Promise<void>;
loadSettings: () => Promise<void>;
}
export const useSettingsStore = create<SettingsStore>((set) => ({
language: "zh-CN",
setLanguage: async (language) => {
const store = await Store.load("settings.json");
await store.set("language", language);
await store.save();
set({ language });
},
loadSettings: async () => {
const store = await Store.load("settings.json");
const language = await store.get<string>("language");
set({ language: language ?? "zh-CN" });
},
}));
插件用法详见 Store 插件。
统一导出
// src/store/index.ts
// 新增 Store 时:1. 创建 src/store/xxx.ts 2. 在此处 export
export { useAppStore } from "./app";
export { useSettingsStore } from "./settings";
resolveTheme 和 ThemeMode 没有从 index.ts 转出,需要时从 @/store/app 导入。
最佳实践
- 选择性订阅:使用选择器
(s) => s.field避免不必要的重渲染 - 按模块分文件:每个 Store 一个文件,通过
index.ts统一导出 - 异步操作:Store 中可以直接写
async函数 - 职责划分:全局 UI 状态(主题 / 侧边栏)放 Zustand;业务持久数据放 Rust SQLite,通过
configApi等 Command 读写