深入浅出 Zustand:前端状态管理的「轻量王者」✨
如果说国家需要有中央银行统一管理货币发行与流通,那么我们的前端项目就需要中央状态管理系统来统筹全局数据。
在 React 生态中,Redux 曾是状态管理的「标准答案」,但繁琐的模板代码、复杂的中间件配置让开发者苦不堪言;Context+useState 虽轻量,却难逃重渲染、跨组件传值的痛点。
而 Zustand 的出现,就像为前端状态管理量身打造的「极简中央银行」------ 轻量、灵活、无侵入,只用几十行代码就能搞定复杂的全局状态管理,成为当下 React 项目中最受欢迎的状态管理方案之一。
一、Zustand 的核心认知 🍬
1. 什么是 Zustand?
Zustand 是由 React 核心团队成员开发的轻量级状态管理库,核心定位是「极简的全局状态管理方案」。如果把前端项目比作一家超市:
- 全局状态就是超市的「库存数据」(比如商品数量、价格、库存状态);
- Zustand 就是超市的「库存管理系统」,专门负责存储、修改、查询这些库存数据;
- 组件就是超市的「各个货架 / 收银台」,通过这个系统获取最新库存,也能修改库存数据。
它摒弃了 Redux 的繁琐模板、Context 的嵌套限制,只用一个create方法就能创建全局状态仓库,组件调用像用普通 Hook 一样简单。
2. 为什么需要 Zustand?
前端项目中,「状态共享」是绕不开的需求:比如购物车数据需要在商品页、结算页、个人中心同步;用户登录状态需要在首页、详情页、设置页共用。没有统一的状态管理系统,会出现这些问题:
- 组件间传值像「传话游戏」,多层嵌套的组件传值要层层透传(props drilling),代码冗余且易出错;
- Context+useState 修改状态时,会导致所有消费 Context 的组件强制重渲染,性能损耗大;
- Redux 需要写 action、reducer、dispatch 等大量模板代码,开发效率低。
而 Zustand 就像「快递直送」,全局状态可以直接被任意组件获取 / 修改,无需层层传递,也不会有多余的重渲染,完美解决以上痛点。
3. Zustand 的核心优势是什么?
- 🚀 极致轻量:核心代码仅 2KB,无任何依赖,接入项目几乎不增加包体积;
- 🎯 无侵入式:无需 Provider 包裹根组件,摆脱 Context 的嵌套地狱;
- 🧠 极简 API:核心只有
create方法,学习成本极低,上手即用; - ⚡ 性能优秀:细粒度状态订阅,只有用到的状态变化时组件才重渲染;
- 🛠️ 灵活度高:原生支持异步状态修改、持久化存储,无需额外中间件;
- 🔌 兼容性好:支持 React Class 组件、函数组件,也能和 TypeScript 完美适配。
二、安装 Zustand 核心包📦
Zustand 无任何依赖,直接安装核心包即可,npm/pnpm 都支持,在项目全局执行:
bash
npm install zustand
# 推荐使用pnpm(速度更快、磁盘占用更少)
pnpm add zustand
三、构建 Store(状态容器)🏪
1. 核心概念
Store 是 Zustand 的核心,是一个全局唯一的状态容器,就像超市的「中央库存仓」,里面包含:
- 项目的全局状态数据(比如计数器的
count值); - 修改状态的方法(比如计数器的
increment加 1、decrement减 1); - 衍生计算属性(比如根据
count计算是否为偶数)。
2. 创建 Store 的基础写法
为了项目代码结构清晰,我们通常创建一个单独的文件夹(src/store/),统一组织管理全局状态;在 store 目录下按业务模块编写状态文件counter.ts`),结构如下:
plaintext
src/
└── store/
└── counter.ts # 计数器状态模块
└── App.tsx

3. 简单的计数器案例实现
第一步:编写状态仓库(src/store/counter.ts)
typescript
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
// 第一步:定义状态类型(TypeScript 必备,约束状态和方法)
interface CounterState {
count: number; // 核心状态:计数器数值
increment: () => void; // 方法:计数器加1
decrement: () => void; // 方法:计数器减1
reset: () => void; // 方法:重置计数器
}
// 第二步:创建状态仓库
const useCounterStore = create<CounterState>((set, get) => ({
// 初始化状态
count: 0,
// 方法1:计数器加1(基于旧状态修改)
increment: () => set((state) => ({ count: state.count + 1 })),
// 方法2:计数器减1(基于旧状态修改)
decrement: () => set((state) => ({ count: state.count - 1 })),
// 方法3:重置计数器(直接修改状态)
reset: () => set({ count: 0 }),
}));
// 导出自定义Hook,供组件使用
export default useCounterStore;
第二步:组件中使用状态(src/App.tsx)
tsx
import './App.css';
import useCounterStore from './store/counter';
import { useState } from 'react';
function App() {
// 解构获取状态和方法
const { count, increment, decrement, reset } = useCounterStore();
return (
<>
{/* 点击按钮触发加1,同时展示当前数值 */}
<button onClick={increment}>
count is {count}
</button>
{/* 点击按钮触发减1 */}
<button onClick={decrement}>
decrement
</button>
{/* 点击按钮重置计数器 */}
<button onClick={reset}>
reset
</button>
</>
);
}
export default App;
代码逻辑详解
- 状态定义层:通过
interface CounterState约束了计数器的状态(count)和修改方法(increment/decrement/reset),TypeScript 加持下,代码提示和类型校验更友好; - 状态创建层:调用
create方法传入回调函数,回调函数返回包含「初始状态 + 修改方法」的对象,create最终返回一个自定义 Hook(useCounterStore); - 组件使用层:在
App组件中直接解构useCounterStore的返回值,获取count状态和修改方法,绑定到按钮的点击事件,实现计数器的核心功能。
代码效果展示

四、Zustand 核心重点🔍
仔细阅读并理解上面案例的代码,我们将根据案例代码拆分并讲解 Zustand 核心知识点。
1. 状态仓库的创建工厂:create 方法
在 Zustand 中,create函数是整个状态管理库的核心。它就像「状态仓库的工厂」,用于创建一个可被 React 组件使用的状态 store(状态容器),并返回一个自定义 Hook(如案例中的useCounterStore)。
这个 Hook 有两个核心特性:
-
可在任意组件中调用,无需像 Redux/Context 那样用 Provider 包裹根组件,彻底摆脱 React Context 的限制;
// Provider 包裹跟组件示例
// 1. 创建 Context
const ThemeContext = createContext();// 2. 用 Provider 包裹组件树,提供数据
function App() {
return (
<ThemeContext.Provider value="dark">
<ChildComponent />
</ThemeContext.Provider>
);
}// 3. 子组件必须在 Provider 内部才能消费数据
function ChildComponent() {
const theme = useContext(ThemeContext); // ← 必须在 Provider 下才能拿到值
return当前主题:{theme};
} -
调用后能直接获取状态或触发状态更新,用法和普通 React Hook 完全一致,学习成本几乎为零。
2. 核心参数说明:create 回调的两个内置函数
调用create()时传入的回调函数,会被自动注入两个核心内置方法(set和get),专门用来操作状态,这是 Zustand 的核心,必须掌握:
(1)set:修改状态的核心方法
set方法的唯一作用是更新 store 中的状态,触发依赖该状态的组件重渲染。所有状态更新都必须通过set完成,不允许直接修改状态(比如state.count = 1是错误写法)。
(2)get:获取当前最新状态
get方法的作用是读取 store 中的当前最新状态,可以在任意修改方法中调用,用于「基于其他状态做逻辑处理」。比如我们想给计数器加一个「加倍」方法:
typescript
// 加倍方法
double: () => {
const currentCount = get().count; // 获取当前最新count值
set({ count: currentCount * 2 }); // count 值乘以2
}
3. 核心重点:修改状态的 3 种主流方式
Zustand 提供了 3 种修改状态的写法,覆盖同步 / 异步、简单 / 复杂所有业务场景,无优先级,按需使用,都是官方推荐的标准写法。
第一种:直接修改状态(最简单,推荐简单场景)
适用于:不需要依赖旧状态、直接赋值更新的场景(如重置计数器、修改用户名、开关状态等)。
语法:set({ 要修改的状态键: 新值 })
代码举例(基于计数器案例):
typescript
// 重置计数器:直接将count设为0,无需依赖旧值
reset: () => set({ count: 0 }),
// 拓展:设置固定值(比如直接设为10)
setFixed: () => set({ count: 10 }),
详解:set方法接收一个对象,对象的键是要修改的状态名(count),值是新的状态值(0/10),调用后状态会立即更新,依赖该状态的组件会重渲染。
第二种:基于旧状态修改(最常用,推荐核心场景)
适用于:需要依赖状态的旧值更新新值的场景(如计数累加、数组追加、对象属性修改等),这是项目中使用频率最高的写法。
语法:set( (prevState) => ({ 要修改的状态键: 基于prevState的新值 }) )
其中prevState是 store 中当前最新的完整状态对象,只读不可直接修改;回调函数返回一个对象,代表要更新的状态键值对。
代码举例(基于计数器案例):
typescript
// 计数器加1:依赖旧的count值,加1后作为新值
increment: () => set((prevState) => ({ count: prevState.count + 1 })),
// 计数器减1:同理,依赖旧值减1
decrement: () => set((prevState) => ({ count: prevState.count - 1 })),
// 拓展:数组追加(通用场景)
// 假设状态中有一个list数组,新增元素
addItem: () => set((prev) => ({
list: [...prev.list, '新元素'] // 基于旧数组创建新数组,避免直接修改原数组
})),
详解:set方法接收一个回调函数,参数prevState是更新前的状态快照,我们基于这个快照计算新值,返回新的状态对象。这种写法保证了状态更新的原子性,避免并发修改导致的错误。
第三种:异步修改状态(原生支持,无需中间件,重中之重)
核心亮点:Zustand 对异步的支持是「原生级」的,不需要任何中间件(比如 Redux 需要 redux-thunk/redux-saga)。适用于:所有需要异步操作后更新状态的场景(接口请求、定时器、本地存储读取等)。
语法:直接在方法中定义async函数,在异步逻辑完成后,调用set方法更新状态即可。
代码举例(基于计数器案例,模拟异步请求后更新状态):
typescript
// 异步加载初始值(比如从接口获取计数器初始值)
fetchInitCount: async () => {
// 模拟接口请求
const res = await fetch('https://api.example.com/count');
const data = await res.json();
// 异步操作完成后,调用set更新状态
set({ count: data.initCount });
},
// 异步加1(模拟延迟操作)
asyncIncrement: async () => {
// 模拟异步延迟(比如接口提交后加1)
await new Promise(resolve => setTimeout(resolve, 1000));
// 基于旧状态修改
set((prev) => ({ count: prev.count + 1 }));
}
详解:异步方法中,我们可以先执行异步逻辑(接口请求、定时器),等结果返回后,再调用set方法更新状态,写法和普通异步代码完全一致,没有额外学习成本。
4. 组件中使用 Store(订阅状态 / 调用方法)
组件中使用 Zustand 的状态和方法,是无侵入式的,直接调用创建好的useStore即可,有两种核心用法:
用法一:通过 selector 选择需要的状态 / 方法(推荐,性能最优)
核心语法:useStore( state => state.xxx )
代码举例(基于计数器案例):
tsx
import useCounterStore from './store/counter';
function App() {
// 只订阅count状态:只有count变化时,组件才重渲染
// 其中,useCounterStore 接受一个selector 函数作为返回值
const count = useCounterStore(state => state.count);
// 只订阅increment方法:方法引用不会变化,组件不会因方法重渲染
const increment = useCounterStore(state => state.increment);
return (
<button onClick={increment}>count is {count}</button>
);
}
核心优势:细粒度订阅,组件只会订阅「selector 返回的这个值」,只有这个值变化时,组件才会重渲染,性能最优,项目中优先使用这种写法。
用法二:解构多个状态 / 方法(便捷,但有坑)
如果组件需要用到多个状态 / 方法,手动写多个 selector 会繁琐,可直接解构:
tsx
// 便捷写法,但有重渲染坑点
const { count, increment, decrement } = useCounterStore();
解构的「重渲染坑点」(高频问题,必看必解决)
问题原因:每次调用useCounterStore()都会返回一个新的对象,即使状态没变化,组件也会因为「对象引用变化」触发重渲染,导致性能损耗。
解决方案:使用shallow浅比较,让 Zustand 只比较对象的属性值,而非引用。
用法步骤:
- 从 zustand 中导入
shallow; - 在
useStore中传入第二个参数shallow。
代码举例:
tsx
import { shallow } from 'zustand/shallow'; // 第一步:导入shallow
import useCounterStore from './store/counter';
function App() {
// 第二步:传入shallow作为第二个参数
const { count, increment, decrement } = useCounterStore(
state => ({
count: state.count,
increment: state.increment,
decrement: state.decrement
}),
shallow // 浅比较:只有属性值变化时,才触发重渲染
);
return (
<>
<button onClick={increment}>count is {count}</button>
<button onClick={decrement}>decrement</button>
</>
);
}
注释详解:shallow会对 selector 返回的对象做浅比较,只有当count、increment、decrement的值发生变化时,组件才会重渲染,解决了引用变化导致的无效重渲染问题。
5. 持久化能力:persist 中间件(本地存储)💾
persist是 Zustand 官方提供的中间件,核心功能是:自动将 Store 的状态保存到持久化存储(如 localStorage、sessionStorage),并在页面刷新或重新加载时自动恢复。
代码举例(基于计数器案例,添加持久化):
typescript
import { create } from 'zustand';
import { persist } from 'zustand/middleware'; // 导入persist中间件
interface CounterState {
count: number;
increment: () => void;
decrement: () => void;
reset: () => void;
}
// 使用persist包裹状态逻辑
const useCounterStore = create<CounterState>()(
persist(
(set, get) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}),
{
name: 'counter-localStorage', // 本地存储的key(必填)
// 可选:指定存储方式,默认localStorage,也可设为sessionStorage
// storage: sessionStorage,
}
)
);
export default useCounterStore;
代码逻辑详解:
- 导入
persist中间件:从zustand/middleware中导入,这是官方内置的中间件,无需额外安装; - 包裹状态逻辑:将
create的回调函数用persist包裹,persist接收两个参数 ------ 状态逻辑函数、配置对象; - 配置存储 key:
name字段是本地存储的唯一标识(比如counter-localStorage),页面刷新后,Zustand 会自动从 localStorage 中读取该 key 对应的数值,恢复count状态; - 可选配置:
storage字段可指定存储方式,默认localStorage,也可设置为sessionStorage,满足不同的持久化需求。
持久化效果展示:

五、核心知识总结(面试官会问)📝
- Zustand 的核心优势:轻量无依赖、无 Provider、细粒度订阅、原生支持异步、TypeScript 友好;
- create 方法的作用:创建状态仓库,返回自定义 Hook,无需 Provider 包裹;
- set 和 get 的区别:set 用于修改状态(必须通过 set 更新,不能直接修改),get 用于获取当前最新状态;
- 修改状态的三种方式:直接修改(简单场景)、基于旧状态修改(核心场景)、异步修改(异步场景);
- 组件使用 Store 的性能优化:优先使用 selector 细粒度订阅,解构时配合 shallow 浅比较避免无效重渲染;
- 持久化实现:通过 persist 中间件,配置 name 和 storage,实现状态本地存储与自动恢复。
六、结语🎈
Zustand 以「极简、灵活、高性能」的特性,成为 React 状态管理的优选方案。它没有 Redux 的繁琐模板,也没有 Context 的性能问题,只用几行代码就能实现全局状态管理,无论是小型项目还是大型应用,都能轻松应对。
本文通过计数器案例,从核心概念、安装、Store 构建、核心 API 到性能优化、持久化,完整拆解了 Zustand 的使用方法。希望这篇文章能帮助你彻底掌握 Zustand,让前端状态管理变得简单又高效。记住:最好的学习方式是动手实践,把案例代码敲一遍,结合自己的项目场景改造,你会发现 Zustand 的魅力所在!