跳到正文
灵动桌面框架
Esc
本栏目菜单

Zustand 状态管理

灵动桌面框架使用 Zustand 5(zustand ^5.0.11)作为全局状态管理方案,轻量、简洁、TypeScript 友好。

框架内置两个 Store:

Store文件状态方法
useAppStoresrc/store/app.tstheme、sidebarCollapsedtoggleTheme、setTheme、toggleSidebar
useSettingsStoresrc/store/settings.tslanguage、closeBehaviorsetLanguage、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 读写