Zustand 简介:轻量级 React 状态管理库 – wiki基地


Zustand 简介:告别繁琐,拥抱简洁高效的 React 状态管理库

在构建复杂的 React 应用程序时,状态管理是一个绕不开的核心议题。随着应用规模的增长,组件之间的状态共享、数据流的维护以及异步操作的处理变得越来越复杂。传统的 useStateuseContext 在处理局部状态或简单的全局状态时表现良好,但面对跨多个组件层级、频繁更新的复杂全局状态时,其局限性就显现出来了,例如“Provider 地狱”、性能瓶颈(不必要的重渲染)以及异步逻辑的复杂性。

正是在这样的背景下,各种 React 状态管理库应运而生,其中 Redux 凭借其严格的单向数据流和庞大的生态系统成为了事实上的标准。然而,Redux 的学习曲线较陡,概念较多(Action, Reducer, Store, Middleware, Thunk/Saga 等),且需要大量的样板代码,即使有了 Redux Toolkit 的简化,对于一些追求极致简洁和开发效率的开发者来说,仍显得有些“重”。

近年来,React 社区涌现出了一批更现代化、更轻量、更依赖 Hook 的状态管理方案,Zustand 就是其中的佼佼者。Zustand 以其简洁、高效、无侵入的特性,迅速获得了开发者们的青睐。它旨在提供一种极简的方式来管理 React 应用中的状态,让你能够告别繁琐的配置和样板代码,专注于业务逻辑本身。

本文将带你深入了解 Zustand,从它的核心理念、基本用法,到高级特性、性能优化以及与其他库的对比,全面解析这个轻量级状态管理库的魅力所在。

一、什么是 Zustand?

Zustand 是一个由 Jotai 和 React-Spring 的作者 Poimandres 团队开发的、基于 Hook 的轻量级状态管理库。它的名字来源于德语,意为“状态”(state)。Zustand 的设计哲学是“KISS”(Keep It Simple, Stupid),它提供了一个最小化的 API,让开发者能够以直观的方式创建、更新和使用全局状态。

与 Redux 需要设置 Provider 来包裹应用不同,Zustand 创建的 Store 存在于 React 组件树之外。这意味着你的组件可以直接从 Store 中获取状态和调用修改状态的方法,而无需通过 Context 或 props 层层传递,从而避免了“Provider 地狱”和部分不必要的重渲染。

Zustand 的核心优势在于:

  1. 极简的 API: 只需一个 create 函数即可创建 Store。
  2. 无冗余: 不需要 boilerplate 代码,也没有 Redux 那样严格的 Action/Reducer 模式(当然你也可以采用类似模式)。
  3. 高性能: 通过选择器 (selectors) 优化组件渲染,只在被使用到的状态改变时触发组件更新。
  4. 基于 Hook: 完全拥抱 React Hook 的范式,使用 useStore Hook 来连接组件与 Store。
  5. 独立于 React: Store 本身是纯 JavaScript 对象/函数,可以在 React 组件之外使用,方便测试和非 React 环境下的逻辑复用。
  6. 可扩展: 支持中间件 (middleware),方便集成 Redux DevTools、持久化、Immer 等功能。

总的来说,Zustand 提供了一种既有 Redux 的全局状态管理能力,又兼具 Context API 的简洁(在某些方面甚至更简洁)和 Hook 的便利性的方案。

二、为什么选择 Zustand?

在众多的 React 状态管理方案中,为什么 Zustand 值得你考虑?

  1. 告别冗余的样板代码:
    如果你使用过 Redux(特别是在 Redux Toolkit 出现之前),你可能会对创建 Action 类型、Action Creators、Reducers、配置 Store 等一系列繁琐的步骤感到头疼。Zustand 极大地简化了这个过程。创建一个 Store 就像定义一个普通 JavaScript 对象一样简单,状态和修改状态的方法都集中在一起,一目了然。这显著提高了开发效率,尤其是在项目初期或中小型项目中。

  2. 直观的状态更新:
    在 Redux 中,你需要派发一个 Action,Reducer 接收 Action 并返回新的状态。这是一种模式化的更新方式。Zustand 则允许你直接在 Store 中定义方法来更新状态,通常通过 set 函数来实现。这种方式更加直观,尤其对于简单的状态更新,代码量更少,逻辑更清晰。

  3. 灵活的组织方式:
    Zustand 不强制你使用特定的文件结构或模式(如 ducks 模式)。你可以根据项目的需求,将相关状态和逻辑组织在一起,或者创建多个独立的 Store 来管理不同领域的状态,这提供了极大的灵活性。

  4. 优秀的性能:
    默认情况下,Zustand 通过 Hook 的选择器机制,确保组件只在其依赖的状态发生变化时重新渲染。这比 useContext 在全局状态频繁更新时导致的整个 Context Consumer 树的重渲染要高效得多。你可以精确控制哪些状态的变化会触发哪些组件的更新。

  5. 易于学习和使用:
    Zustand 的核心 API 非常小巧,学习曲线平缓。如果你熟悉 Hook,上手 Zustand 会非常快。它的概念也很少,主要就是 Store 和 useStore Hook。

  6. 良好的 TypeScript 支持:
    Zustand 使用 TypeScript 编写,提供了良好的类型定义,这对于使用 TypeScript 进行开发的项目来说非常友好。

  7. 强大的社区支持与生态:
    虽然不像 Redux 那样拥有庞大的生态,但 Zustand 社区活跃,并且得益于其简洁的设计,可以方便地集成许多现有的工具和库,例如 Immer、Redux DevTools、各种持久化方案等,这些都通过中间件的方式提供。

三、Zustand 的核心概念与基本用法

Zustand 的核心非常简单:一个 create 函数用来创建一个 Store,一个 useStore Hook 用来在组件中访问 Store。

1. 创建一个 Store

使用 create 函数创建一个 Store。create 函数接收一个函数作为参数,这个函数接收 setget 两个参数,并返回一个对象,这个对象就是你的 Store 状态和操作。

  • set: 用于更新 Store 的状态。它接受一个函数或一个对象作为参数。如果传入函数,函数接收当前状态作为参数,返回要合并的新状态。如果传入对象,则直接将对象合并到当前状态。
  • get: 用于获取 Store 的当前状态。

“`javascript
// stores/counterStore.js
import { create } from ‘zustand’;

// 创建一个 Store
const useCounterStore = create((set, get) => ({
// 状态
count: 0,
title: ‘Counter’,

// 操作 (Actions)
increase: (by) => set((state) => ({ count: state.count + by })),
decrease: (by) => set((state) => ({ count: state.count – by })),
reset: () => set({ count: 0 }), // 直接设置对象方式更新

// 操作中可以使用 get 获取当前状态
logCount: () => {
const currentCount = get().count;
console.log(‘Current count:’, currentCount);
},

// 异步操作示例 (将在高级用法中详细介绍)
// asyncIncrease: async (by) => {
// await delay(1000); // 模拟异步延迟
// set((state) => ({ count: state.count + by }));
// }
}));

export default useCounterStore;
“`

上面的代码创建了一个名为 useCounterStore 的 Store。它包含了 counttitle 两个状态,以及 increase, decrease, reset, logCount 四个修改或读取状态的方法。注意,我们导出的不是 Store 对象本身,而是 create 函数返回的 Hook 函数 useCounterStore。这个 Hook 就是我们之后在组件中使用的接口。

2. 在组件中使用 Store

在 React 组件中,使用上面导出的 useCounterStore Hook 来访问状态和操作。

“`jsx
// components/Counter.jsx
import React from ‘react’;
import useCounterStore from ‘../stores/counterStore’;

function Counter() {
// 使用 Hook 获取状态和操作
// 方法一:获取整个 store 对象 (不推荐,可能导致不必要的重渲染)
// const store = useCounterStore();
// const count = store.count;
// const increase = store.increase;

// 方法二:通过选择器精确选择需要使用的状态和操作 (推荐)
const count = useCounterStore(state => state.count); // 只订阅 count 状态
const increase = useCounterStore(state => state.increase); // 只订阅 increase 操作 (操作通常不变,但这样写更清晰)
const decrease = useCounterStore(state => state.decrease);
const reset = useCounterStore(state => state.reset);
const title = useCounterStore(state => state.title);
const logCount = useCounterStore(state => state.logCount);

console.log(‘Counter component rendered’); // 观察渲染情况

return (

{title}

Count: {count}




);
}

export default Counter;
“`

在上面的 Counter 组件中,我们使用了 useCounterStore Hook。注意我们是如何传递一个选择器函数 (state => state.count) 给 useCounterStore 的。这个选择器函数接收当前 Store 的状态作为参数,并返回组件需要使用的那部分状态或操作。

重要概念:选择器与性能优化

这是 Zustand 实现高性能的关键。当你像这样使用 useStore(selector) 时,Zustand 会只在你通过 selector 选择的结果发生变化时才触发组件的重新渲染。

例如:
const count = useCounterStore(state => state.count); 只在 state.count 变化时,Counter 组件才会重新渲染。
– 如果你写成 const store = useCounterStore(); 然后再访问 store.count, store.increase 等,那么 Store 中的任何状态变化(包括 title 的变化)都可能导致 Counter 组件重新渲染,即使 count 没有变。这是因为 Hook 返回的 store 对象引用发生了变化。

因此,强烈推荐使用选择器来精确地订阅 Store 的一部分状态,这能够最大限度地优化组件的渲染性能。对于操作函数,虽然它们通常是固定的引用,但为了保持代码风格一致和明确表达组件的依赖,通常也通过选择器来获取。

3. 在组件外使用 Store

Zustand 的 Store 独立于 React 组件树,这意味着你可以在任何地方访问和修改 Store 的状态,例如在纯 JavaScript 模块中处理业务逻辑或进行测试。

“`javascript
// services/counterService.js
import useCounterStore from ‘../stores/counterStore’;

// 获取 Store 的非 Hook 访问方式
const counterStore = useCounterStore.getState(); // 获取当前状态的快照

// 可以订阅状态变化 (例如用于日志记录或其他非 React 逻辑)
const unsubscribe = useCounterStore.subscribe(
(state, prevState) => {
console.log(‘Counter state changed:’, state.count, ‘from’, prevState.count);
},
(state) => state.count // 订阅 count 状态的变化
);

// 可以在组件外调用 Store 的操作
function incrementCounterInService() {
useCounterStore.getState().increase(5); // 获取状态并调用操作
}

incrementCounterInService(); // 调用增加函数

// 后续不再需要监听时,可以取消订阅
// unsubscribe();
“`

useCounterStore.getState() 提供了一个获取当前状态快照的方法。
useCounterStore.subscribe() 允许你在 Store 状态变化时执行副作用,可以传入一个选择器来监听特定状态的变化。

这种能力使得 Zustand 的状态逻辑更容易剥离出 React 组件,提高代码的可维护性和可测试性。

四、高级特性与中间件

Zustand 通过中间件机制提供了强大的扩展能力,可以方便地集成各种功能。中间件是用于增强 create 函数功能的包装器。

使用中间件时,你需要将 create 的返回值传递给中间件函数。通常采用函数链式调用的方式:

“`javascript
import { create } from ‘zustand’;
import { devtools, persist } from ‘zustand/middleware’;

const useEnhancedStore = create(
// 使用 devtools 中间件
devtools(
// 使用 persist 中间件
persist(
(set, get) => ({
// 你的 Store 定义
count: 0,
increase: (by) => set((state) => ({ count: state.count + by })),
}),
{
name: ‘enhanced-storage’, // 在 localStorage 中存储的 key
// 可选: 定义哪些状态需要被存储,哪些不需要 (默认全部存储)
// partialize: (state) => ({ count: state.count }),
// 可选: 自定义存储方式 (默认使用 localStorage)
// storage: createJSONStorage(() => sessionStorage),
}
)
)
);
“`

下面介绍几个常用的中间件:

1. devtools: 集成 Redux DevTools

这是调试 Zustand Store 的利器。通过 devtools 中间件,你可以将 Zustand Store 的状态变化暴露给 Redux DevTools 浏览器扩展,从而方便地查看状态历史、回放操作等。

“`javascript
import { create } from ‘zustand’;
import { devtools } from ‘zustand/middleware’;

const useDevToolsStore = create(
devtools(
(set) => ({
bears: 0,
increaseBears: () => set(state => ({ bears: state.bears + 1 }), false, ‘increaseBears’), // 第三个参数是给 devtools 的 action type
decreaseBears: () => set(state => ({ bears: state.bears – 1 }), false, ‘decreaseBears’),
}),
{
name: ‘MyZustandStore’, // 在 DevTools 中显示的 Store 名称
// 可选: serialize: { options: true } // 启用序列化选项
}
)
);
“`

在使用 set 更新状态时,可以传入第三个参数作为在 Redux DevTools 中显示的 Action 类型,这有助于跟踪状态变化的来源。第二个参数 false 表示不合并(这是 set 的默认行为,通常可以省略)。

2. persist: 状态持久化

persist 中间件允许你将 Store 的状态存储到客户端的存储中(如 localStoragesessionStorage),以便在页面刷新后恢复状态。

“`javascript
import { create } from ‘zustand’;
import { persist, createJSONStorage } from ‘zustand/middleware’;

const usePersistStore = create(
persist(
(set, get) => ({
cart: [],
addItem: (item) => set(state => ({ cart: […state.cart, item] })),
// … 其他购物车操作
}),
{
name: ‘shopping-cart’, // 在存储中使用的 key
// 可选: 使用 sessionStorage 代替 localStorage
// storage: createJSONStorage(() => sessionStorage),
// 可选: 只持久化部分状态
// partialize: (state) => ({ cart: state.cart }),
// 可选: 状态重新水合时的回调
// onRehydrateStorage: (state) => {
// console.log(‘State rehydrated’, state);
// },
// 可选: 版本控制
// version: 1,
// migrate: (persistedState, version) => {
// if (version === 0) {
// // 根据旧版本状态进行迁移
// return { …persistedState, newField: ‘defaultValue’ };
// }
// return persistedState;
// }
}
)
);
“`

persist 的第二个参数是一个配置对象,其中 name 是必须的。你可以通过 storage 参数指定不同的存储方式,通过 partialize 参数指定只存储部分状态,通过 versionmigrate 实现状态结构的迁移。

3. immer: 使用 Immer 简化不可变更新

Immer 是一个流行的库,它允许你使用可变的语法来编写不可变的状态更新逻辑。Zustand 提供了 immer 中间件,让你可以在 Store 中方便地使用 Immer。

首先需要安装 Immer:npm install immer use-immer

“`javascript
import { create } from ‘zustand’;
import { immer } from ‘zustand/middleware/immer’;

const useImmerStore = create(
immer(
(set) => ({
user: {
name: ‘Guest’,
address: {
street: ‘Unknown’,
zip: ‘00000’,
},
tags: [‘anonymous’],
},
updateUserName: (name) => set(state => {
state.user.name = name; // 直接修改 draft state,Immer 会生成新的不可变状态
}),
addTag: (tag) => set(state => {
state.user.tags.push(tag); // 直接修改数组
}),
// … 其他复杂更新
})
)
);
“`

使用 immer 中间件后,set 函数接收的参数就变了。它现在接收一个函数,这个函数的参数是一个 draft 状态对象,你可以直接对 draft 对象进行修改,Immer 会自动帮你生成不可变的新状态。这对于处理嵌套结构或数组的状态更新非常方便,避免了手动拷贝和展开。

4. 其他中间件

Zustand 生态中还有其他一些有用的中间件或相关库,例如用于调试状态变化的 log 中间件(通常结合 devtools 使用),或者社区贡献的各种中间件。

五、异步操作

在状态管理中处理异步操作(如数据请求)是一个常见的需求。Zustand 原生支持在 Store 的 Action 中使用 async/await

“`javascript
import { create } from ‘zustand’;
import { devtools } from ‘zustand/middleware’;

const useFetchStore = create(
devtools(
(set) => ({
data: null,
isLoading: false,
error: null,

  fetchData: async (url) => {
    set({ isLoading: true, error: null }); // 开始加载,设置 loading 状态

    try {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      const result = await response.json();
      set({ data: result, isLoading: false }); // 请求成功,更新数据和 loading 状态
    } catch (error) {
      console.error("Failed to fetch data:", error);
      set({ error: error.message, isLoading: false }); // 请求失败,更新错误和 loading 状态
    }
  },
})

)
);

export default useFetchStore;
“`

在组件中使用这个 Store:

“`jsx
import React, { useEffect } from ‘react’;
import useFetchStore from ‘../stores/fetchStore’;

function DataFetcher({ url }) {
const { data, isLoading, error, fetchData } = useFetchStore(state => ({
data: state.data,
isLoading: state.isLoading,
error: state.error,
fetchData: state.fetchData, // 获取异步 action
}));

// 在组件挂载后调用异步 action
useEffect(() => {
fetchData(url);
}, [url, fetchData]); // 注意依赖项

if (isLoading) {
return

Loading…

;
}

if (error) {
return

Error: {error}

;
}

return (

Fetched Data:

{JSON.stringify(data, null, 2)}

);
}

export default DataFetcher;
“`

可以看到,在 Zustand 中处理异步操作非常直接,只需要在 Store 的 Action 方法中编写 async 函数,并在其中调用 set 来更新状态即可。这比 Redux 中配置 Thunk 或 Saga 要简单得多。

六、Zustand 与其他状态管理库的对比

了解 Zustand 的定位,需要将其与其他流行的 React 状态管理方案进行比较。

  1. Zustand vs Context API:

    • 简洁性: 对于非常简单的全局状态,Context API 可能看起来更简洁。但当状态更新频繁或 Consumer 树较深时,Context API 容易导致性能问题(不必要的重渲染),且管理多个相关状态和更新逻辑不如 Store 集中方便。
    • 性能: Zustand 通过选择器实现了更精细的订阅和重渲染控制,通常比 Context API 性能更好,尤其是在大型应用中。
    • Provider: Context API 需要 Provider 包裹应用树的一部分。Zustand Store 独立于组件树,大多数情况下不需要 Provider(除非你想覆盖默认 Store 实例,但这不常见)。
    • 复杂性: 管理复杂的更新逻辑和副作用时,Zustand 的 Store 模式更清晰,可以将相关逻辑组织在一起。
  2. Zustand vs Redux:

    • boilerplate: Redux(即使使用 Redux Toolkit)通常需要更多的概念和样板代码。Zustand 极其简洁,快速上手。
    • 强制结构: Redux 强制使用 Action/Reducer 模式,这有助于大型团队的代码规范和可预测性,但也增加了学习成本和代码量。Zustand 更灵活,不强制特定模式,你可以自由组织。
    • 异步处理: Redux 需要 Thunk 或 Saga 中间件来处理异步。Zustand 原生支持 async/await 在 Store Action 中使用,更直观。
    • 生态: Redux 拥有更成熟和庞大的生态系统和工具(如 Redux DevTools 是标准,而 Zustand 需要中间件)。
    • 性能: 都可以通过选择器达到优秀的性能,但 Zustand 的默认使用方式(通过选择器获取状态)就鼓励了性能最佳实践。
    • 大小: Zustand 库体积非常小。
  3. Zustand vs Recoil / Jotai:

    • Recoil 和 Jotai 是基于“原子”(atom) 模型的 Hook 状态管理库。每个状态单元或派生状态都是一个“原子”。
    • Zustand 维护一个大的 Store 对象/函数来管理所有相关状态和逻辑。Recoil/Jotai 将状态分散在多个原子中。
    • 选择哪种取决于个人偏好和项目需求。原子模型对于管理许多相互独立的、细粒度的状态可能更直观。而 Zustand 的单一 Store 模型(或者少数几个 Store)对于组织一个领域内的相关状态和操作可能更方便。
    • Zustand 通常被认为比 Recoil 和 Jotai 更早成熟,文档更丰富,社区用户更多一些,特别是与传统状态管理模式(如 Redux)的迁移成本更低。而 Jotai 则以其更极致的原子化和无侵入性著称。

总结比较:

特性 Context API Redux Recoil / Jotai Zustand
核心概念 Provider, Consumer, useContext Store, Action, Reducer, Dispatch Atom, Selector Store, create, useStore
学习曲线 低 (基础 Hook) 中/高 中 (原子模型) 低/中
样板代码 低 (简单), 高 (复杂结构) 高 (传统), 中 (RTK) 低/中
性能 简单场景好, 复杂场景易受影响 可通过选择器优化 可通过选择器和派生状态优化 可通过选择器优化
异步处理 手动处理或结合其他库 Thunk / Saga 等中间件 需要额外的 Hook 或工具 原生支持 async/await
调试 依赖 React DevTools Redux DevTools (标准) React DevTools, 特定工具 Redux DevTools (中间件)
大小 无额外库 (React 内置) 非常小
灵活性 简单场景灵活, 复杂场景受限 结构化, 约定大于配置 非常灵活 非常灵活, 可选结构
TypeScript 原生支持 良好支持 良好支持 良好支持

从这个对比可以看出,Zustand 在简洁性、样板代码、异步处理和易用性方面具有显著优势,同时在性能和可扩展性方面表现良好,使其成为许多现代 React 应用的有力选择。

七、优点与缺点

优点 (Advantages):

  1. 极度简洁: API 简单直观,学习成本低。
  2. 开发效率高: 极少的样板代码,快速实现状态管理逻辑。
  3. 高性能: 通过 Hook 选择器实现精确的状态订阅,避免不必要的重渲染。
  4. 灵活: 不强制特定的代码结构或模式,可以根据项目需求自由组织。
  5. 无需 Provider: Store 独立于 React 组件树,集成更方便,避免 Provider 地狱。
  6. 原生支持异步: 在 Store Action 中直接使用 async/await 编写异步逻辑。
  7. 可测试性强: Store 是纯 JavaScript 对象/函数,易于在 React 环境外部进行单元测试。
  8. 体积小巧: 库本身的代码量非常少,对打包体积影响小。
  9. 可扩展: 通过中间件机制方便集成各种功能,如 DevTools、持久化、Immer 等。
  10. 良好的 TypeScript 支持: 提供完整的类型定义。

缺点 (Disadvantages) 或注意事项 (Considerations):

  1. 生态系统相对年轻: 虽然发展迅速,但与 Redux 庞大成熟的生态相比,工具和社区资源相对较少(尽管常用的中间件基本都有)。
  2. 缺乏强制结构: 对于大型团队或项目,Zustand 的高度灵活性可能需要团队自行约定状态管理的代码规范,否则可能导致组织混乱(而 Redux 的严格结构有助于规范)。
  3. 全局状态的隐式依赖: 尽管无需 Provider,但组件通过 Hook 直接访问 Store 构成了对全局状态的隐式依赖。虽然这提高了便利性,但也需要注意依赖管理,避免过度耦合。
  4. 选择器是关键: 确保组件性能高度依赖于正确使用选择器来精确订阅所需状态,否则仍可能遇到性能问题。

八、最佳使用场景

Zustand 是许多 React 应用的优秀选择,尤其适用于:

  • 中小型应用: 简洁的 API 和快速的开发速度使其非常适合中小型项目。
  • 希望从 Context API 迁移但遇到性能问题: 如果你的 Context API 状态更新频繁导致性能瓶颈,Zustand 是一个很好的替代方案。
  • 希望从 Redux 迁移以减少样板代码: 如果你觉得 Redux boilerplate 过多,希望寻找一个更轻量、更简洁的方案,Zustand 是一个有吸引力的选择。
  • 对性能有较高要求: 通过精细的选择器控制,Zustand 能够实现出色的渲染性能。
  • 追求开发效率和简洁性: 如果你喜欢 Hook-based 的开发模式,并希望用最少的代码实现状态管理,Zustand 非常适合你。
  • 需要方便地在 React 组件外访问状态: Store 的独立性使得在非 React 环境下使用状态成为可能。

对于极其大型、状态极其复杂、需要严格的代码结构和庞大生态支持的项目,Redux 及其周边工具(如 Redux Saga 用于复杂副作用管理)可能仍然是更稳健的选择。但对于大多数日常应用而言,Zustand 的能力已经足够强大且更加易用。

九、总结与展望

Zustand 凭借其简洁、高效和灵活的特性,在 React 状态管理领域开辟了一条新的道路。它成功地融合了 Hook 的便利性、Store 的集中管理能力以及优秀的性能优化机制,极大地降低了状态管理的复杂性和开发成本。

告别繁琐的配置和样板代码,拥抱直观的状态更新和 Hook 式的使用体验,Zustand 正在成为越来越多 React 开发者的首选状态管理库。无论你是一个经验丰富的开发者,还是刚刚接触状态管理的新手,Zustand 都值得你花时间去学习和尝试。

随着社区的不断壮大和功能的持续完善,我们有理由相信 Zustand 将在未来的 React 生态中扮演越来越重要的角色。如果你正在寻找一个轻量级、高性能、易于使用的 React 状态管理方案,不妨给 Zustand 一个机会,它很可能会给你带来惊喜。


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部