redux的全局状态工具使用时候觉得有点太过繁琐,发现有zustand 一款类似pinia的状态管理工具
1. Zustand 简介
Zustand 是由 Poimandres 团队开发的状态管理库。它的主要特点包括:
- 极简 API:几乎不需要配置或样板代码。
- 与 React 并发模式兼容:Zustand 可以与 React 18 的并发特性兼容。
- 灵活性:你可以轻松创建局部或全局的状态,并且支持异步状态和中间件。
- 性能优化:内置了性能优化的工具,不会像 Redux 那样有多余的状态订阅,也不需要全局挂载,随取随用
[官网] (https://awesomedevin.github.io/zustand-vue/)
相比于 Redux 和 Redux Toolkit 这样的库,Zustand 的确具有更轻量、灵活的状态管理方式,不需要像 Redux 那样将所有状态集中到一个全局的 reducer 中统一管理。Zustand 允许你自由地在组件中导入和使用独立的 store,避免了繁琐的配置和状态耦合问题(redux 还需要全局挂载文件树)。这种自由的方式在许多场景中更加便捷。
为什么 Zustand 更灵活?
- 无需单一的全局状态树:Redux 强调集中化的全局状态树,所有的状态变更都需要通过 reducer 和 action 来管理,导致在大型项目中 reducer 代码可能会变得复杂臃肿。而 Zustand 则允许你为每个功能模块创建独立的 store,按需导入,减少了状态树的复杂性。
- 没有模板化的代码:Redux 的使用通常涉及大量模板化的代码,比如
actions
、reducers
、dispatch
、thunks
等,尤其是当应用变得复杂时,管理这些逻辑会显得冗长。而 Zustand 的 API 非常简单,通过一个create
函数就能直接创建 store,代码简洁明了。 - 按需使用:在 Zustand 中,每个组件只会重新渲染与它相关的状态部分。而在 Redux 中,虽然有
connect
和useSelector
等优化手段,但由于状态集中在全局 store 中,状态管理往往会造成性能负担。 - 无需配置中间件:在 Redux 中,常常需要引入中间件(如
redux-thunk
或redux-saga
)来处理异步操作或扩展功能。Zustand 则内置了对异步处理和其他高级功能的支持,不需要复杂的中间件配置即可使用诸如persist
、devtools
等功能。
简单对比 Zustand 和 Redux:
特性 | Zustand | Redux |
---|---|---|
状态管理结构 | 多个独立 store,自由组合 | 单一全局状态树,需要 combineReducers |
初始配置 | 几乎零配置,直接使用 | 需要初始化 store、reducers、actions 等 |
使用方式 | 自由按需导入 | 全局使用,必须通过 useSelector 等访问 |
异步处理 | 直接在 store 中使用 async/await | 需要引入中间件,如 redux-thunk 或 redux-saga |
性能优化 | 内置选择器,组件只更新相关状态 | 需要手动优化 useSelector 或 connect |
开发工具支持 | 支持 zustand-devtools | Redux DevTools |
模板化代码 | 无需 actions 和 reducers,逻辑更简单 | 必须定义 actions、reducers 和 dispatch |
适用场景的比较
- Zustand 非常适合中小型应用或项目中的独立模块。它的灵活性和简单的 API 让它适用于不需要过于复杂的状态管理场景,甚至在大型项目中,也可以利用其模块化特性按需管理状态。
- Redux 则更适合那些需要复杂的状态管理方案,特别是在涉及到跨多个组件、多个模块间的数据流共享时,它的集中化状态管理和严格的约束有助于维护一致性。
2. 安装 Zustand
Zustand 的安装非常简单。你只需在现有的 React 项目中通过 npm 或 yarn 安装即可:
# 使用 npm 安装
npm install zustand
# 使用 yarn 安装
yarn add zustand
3. 基础用法
3.1 创建 Store
Zustand 中的 store 是通过 create
函数创建的。每个 store 都包含状态和处理状态的函数,类似于 Redux 中的 reducer 和 action。
创建一个简单的计数器 store:
// src/store/useCounterStore.js
import { create } from 'zustand';
// 创建 Zustand store
const useCounterStore = create((set) => ({
count: 0, // 初始状态
increase: () => set((state) => ({ count: state.count + 1 })), // 增加 count
decrease: () => set((state) => ({ count: state.count - 1 })), // 减少 count
}));
export default useCounterStore;
在这个 store 中:
count
: 是我们要管理的状态,初始值为 0。increase
和decrease
: 是两个函数,分别用来增加或减少count
的值。set
函数用于更新状态。
3.2 在组件中使用
现在,我们可以在 React 组件中使用刚刚创建的 store。你可以通过 useCounterStore
钩子来获取状态和操作函数。
// src/components/Counter.js
import React from 'react';
import useCounterStore from '../store/useCounterStore';
const Counter = () => {
// 从 Zustand 中提取状态和操作
const { count, increase, decrease } = useCounterStore();
return (
<div style={{ textAlign: 'center', marginTop: '50px' }}>
<h1>{count}</h1>
<button onClick={increase} style={{ marginRight: '10px' }}>Increase</button>
<button onClick={decrease}>Decrease</button>
</div>
);
};
export default Counter;
这样我们就完成了一个简单的计数器应用。在页面上你可以点击按钮来增加或减少计数器的值。这样就可以按需导入
3.3 状态持久化和本地存储
有时候,你可能希望状态在页面刷新后依然存在。这时你可以使用 Zustand 提供的 persist
中间件将状态保存到本地存储。
// src/store/useCounterStore.js
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
const useCounterStore = create(
persist(
(set) => ({
count: 0,
increase: () => set((state) => ({ count: state.count + 1 })),
decrease: () => set((state) => ({ count: state.count - 1 })),
}),
{
name: 'counter-storage', // 本地存储的键
}
)
);
export default useCounterStore;
这样状态会被存储在浏览器的 localStorage
中,页面刷新后状态依然存在。
4. 高级用法
4.1 使用中间件
Zustand 支持中间件,例如持久化、日志记录或调试工具。除了 persist
,你还可以使用其他中间件,比如 devtools
来与 Redux DevTools 一起工作。
安装 zustand
的 devtools 中间件:
npm install zustand-middleware
使用 devtools
中间件:
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
const useCounterStore = create(
devtools((set) => ({
count: 0,
increase: () => set((state) => ({ count: state.count + 1 })),
decrease: () => set((state) => ({ count: state.count - 1 })),
}))
);
现在,你可以在 Redux DevTools 中监控 Zustand 的状态变化。
4.2 异步状态管理
在实际项目中,你可能会需要处理异步操作,比如从 API 获取数据。Zustand 可以轻松处理异步状态。
// src/store/useUserStore.js
import { create } from 'zustand';
const useUserStore = create((set) => ({
users: [],
fetchUsers: async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
const data = await response.json();
set({ users: data });
},
}));
export default useUserStore;
在组件中使用异步状态:
// src/components/UserList.js
import React, { useEffect } from 'react';
import useUserStore from '../store/useUserStore';
const UserList = () => {
const { users, fetchUsers } = useUserStore();
useEffect(() => {
fetchUsers(); // 组件挂载时获取用户数据
}, [fetchUsers]);
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
};
export default UserList;
4.3 状态选择器
Zustand 支持使用状态选择器来选择状态的某一部分,避免不必要的组件重新渲染。
// 选择 count 状态而不是整个 store
const count = useCounterStore((state) => state.count);
这样只有 count
发生变化时,组件才会重新渲染。
4.4 Zustand 中的 Immer
Zustand 内部集成了 Immer.js,让你能够直接修改状态,而不需要担心状态的不可变性。
const useStore = create((set) => ({
items: [],
addItem: (item) =>
set((state) => {
state.items.push(item);
}),
}));
5. React 18 可以使用的的新特新
Zustand 完全兼容 React 18,并且它的并发特性不会影响 Zustand 的性能。Zustand 通过非常轻量的状态订阅机制,天然支持 React 18 中的并发模式。
React 18 中的自动批处理(Automatic Batching)和并发渲染(Concurrent Rendering)对 Zustand 状态更新同样适用,因此你可以在 React 18 项目中无缝使用 Zustand。
6. 常见问题和最佳实践
6.1 Zustand 与 Redux 的区别
- Zustand 更轻量:相比于 Redux 的样板代码和复杂配置,Zustand 更简洁。
- 无需 reducer 和 action:Zustand 直接使用 setter 和 getter,而无需引入 reducer 和 action。
- 更灵活:Zustand 适用于小型和中型项目,对于大型项目 Redux 可能会有更好的生态支持(如中间件、工具链)。
6.2 状态选择器与性能优化
使用状态选择器可以避免不必要的组件渲染,这是优化 Zustand 性能的重要方式。