Zustand 快速入门:5 分钟上手极简状态管理

Zustand 的核心优势是 API 极简、零模板代码、无需 Provider ,只需掌握 create(创建 Store)和 useStore(组件订阅)两个核心 API,就能快速实现全局/局部状态共享。下面从「安装 → 基础使用 → 核心特性 → 实战示例」一步步带你入门:

一、环境准备与安装

1. 安装依赖

支持 React(≥16.8,需 Hooks 支持)、Vue(通过适配器)、原生 JS,这里以 React + TS 为例(JS 项目可忽略类型定义):

bash 复制代码
# npm
npm install zustand --save

# yarn
yarn add zustand

# pnpm
pnpm add zustand

2. 兼容性

  • React:≥16.8(支持 Hooks)
  • TypeScript:≥4.1(自动类型推导,无需额外配置)
  • 浏览器:支持 ES6+ 的现代浏览器(IE 不兼容)

二、核心概念速览

Zustand 没有复杂概念,核心只有 2 个:

  • Store:状态容器,存储全局共享的状态和修改状态的方法(类似「数据 + 方法」的集合)。
  • useStore:React Hooks,用于在组件中订阅 Store 的状态、调用 Store 的方法。

无需 Provider 包裹应用、无需 Action Type、无需 Reducer,直接定义、直接使用!

三、基础使用:3 步实现全局状态

以「全局计数器」为例,快速体验 Zustand 的简洁:

1. 第一步:创建 Store

新建 stores/counterStore.ts(按模块拆分 Store,模块化更清晰):

typescript 复制代码
import { create } from 'zustand';

// 创建 Store:参数是一个函数,返回「状态 + 修改方法」
const useCounterStore = create((set) => ({
  // 1. 状态(类似 React 的 useState)
  count: 0,

  // 2. 修改状态的方法(直接定义,无需 Action/Reducer)
  // set:用于修改状态的工具函数,接收新状态或状态更新函数
  increment: () => set((state) => ({ count: state.count + 1 })), // 函数式更新(依赖旧状态)
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }), // 直接设置新状态(不依赖旧状态)
  setCount: (newCount: number) => set({ count: newCount }), // 接收参数修改状态
}));

export default useCounterStore;
关键说明:
  • create 函数返回一个自定义 Hooks(如 useCounterStore),组件通过这个 Hooks 访问状态。
  • set 函数:修改状态时必须通过 set(保证状态更新的可追踪性),支持两种写法:
    • 函数式:set(state => ({ ... }))(依赖旧状态,如计数递增)
    • 对象式:set({ key: value })(不依赖旧状态,如重置)

2. 第二步:组件中使用 Store

在任意组件中直接导入 useCounterStore,无需其他配置:

tsx 复制代码
// CounterComponent.tsx
import useCounterStore from '@/stores/counterStore';

const CounterComponent = () => {
  // 方式 1:订阅单个状态(推荐,性能更优:仅 count 变更时组件重渲染)
  const count = useCounterStore((state) => state.count);

  // 方式 2:订阅多个状态/方法(解构,注意:状态变更会导致组件重渲染)
  const { increment, decrement, reset, setCount } = useCounterStore();

  return (
    <div>
      <h1>计数:{count}</h1>
      <button onClick={increment}>+1</button>
      <button onClick={decrement}>-1</button>
      <button onClick={reset}>重置</button>
      <button onClick={() => setCount(100)}>设置为 100</button>
    </div>
  );
};

export default CounterComponent;
性能优化技巧:
  • 订阅单个状态(useCounterStore(state => state.count)):组件仅在该状态变更时重渲染,比解构整个 Store 性能更优。
  • 若需订阅多个状态,可使用 shallow 比较(下文「核心特性」会讲)。

3. 第三步:多组件共享状态

再创建一个组件,同样导入 useCounterStore,即可共享同一状态:

tsx 复制代码
// CounterDisplay.tsx
import useCounterStore from '@/stores/counterStore';

const CounterDisplay = () => {
  // 订阅 count 状态
  const count = useCounterStore((state) => state.count);

  return <div>全局计数(另一个组件):{count}</div>;
};

export default CounterDisplay;

此时,点击 CounterComponent 的按钮,CounterDisplay 的计数会自动同步更新------全局状态共享就这么简单!

四、核心特性:解决实际开发场景

1. 订阅多个状态(避免冗余渲染)

直接解构多个状态会导致「任意状态变更都触发组件重渲染」,可使用 shallow 比较优化:

typescript 复制代码
import { create } from 'zustand';
import { shallow } from 'zustand/shallow'; // 导入浅比较工具

const useUserStore = create((set) => ({
  name: '张三',
  age: 20,
  setName: (name: string) => set({ name }),
  setAge: (age: number) => set({ age }),
}));

// 组件中使用:仅当 name 或 age 实际变更时,组件才重渲染
const UserComponent = () => {
  const { name, age } = useUserStore(
    (state) => ({ name: state.name, age: state.age }),
    shallow // 第二个参数:浅比较(对比对象的第一层属性)
  );

  return <div>姓名:{name},年龄:{age}</div>;
};

2. 异步状态处理(如接口请求)

Zustand 支持直接在方法中写异步逻辑,无需额外中间件(如 Redux 的 Thunk):

typescript 复制代码
// stores/userStore.ts
import { create } from 'zustand';
import axios from 'axios';

const useUserStore = create((set) => ({
  userInfo: null,
  loading: false,
  error: null,

  // 异步获取用户信息
  fetchUser: async (userId: number) => {
    set({ loading: true, error: null }); // 开始请求:设置加载中
    try {
      const res = await axios.get(`/api/user/${userId}`);
      set({ userInfo: res.data, loading: false }); // 请求成功:更新状态
    } catch (err) {
      set({ error: err.message, loading: false }); // 请求失败:捕获错误
    }
  },
}));

// 组件中使用
const UserProfile = () => {
  const { userInfo, loading, error, fetchUser } = useUserStore();

  useEffect(() => {
    fetchUser(1); // 组件挂载时请求用户信息
  }, [fetchUser]);

  if (loading) return <div>加载中...</div>;
  if (error) return <div>错误:{error}</div>;
  if (!userInfo) return null;

  return <div>姓名:{userInfo.name},邮箱:{userInfo.email}</div>;
};

3. 多 Store 设计(模块化)

Zustand 推荐按业务模块拆分多个独立 Store(如用户、购物车、全局配置),避免单个 Store 过于庞大:

复制代码
stores/
├── userStore.ts   // 用户相关状态
├── cartStore.ts   // 购物车相关状态
└── configStore.ts // 全局配置(如主题、语言)

示例:购物车 Store(cartStore.ts):

typescript 复制代码
import { create } from 'zustand';

const useCartStore = create((set) => ({
  cartItems: [], // 购物车商品列表:[{ id, name, price, quantity }]

  // 添加商品到购物车
  addToCart: (item) => set((state) => ({
    cartItems: [...state.cartItems, { ...item, quantity: 1 }],
  })),

  // 更新商品数量
  updateQuantity: (id, quantity) => set((state) => ({
    cartItems: state.cartItems.map((item) => 
      item.id === id ? { ...item, quantity } : item
    ),
  })),

  // 清空购物车
  clearCart: () => set({ cartItems: [] }),
}));

export default useCartStore;

4. 状态持久化(本地存储)

使用 zustand-persist 插件,轻松实现状态持久化(刷新页面不丢失):

安装插件:
bash 复制代码
npm install zustand-persist --save
实现持久化:
typescript 复制代码
import { create } from 'zustand';
import { persist } from 'zustand-persist';

const useThemeStore = create(
  persist(
    (set) => ({
      theme: 'light', // 主题:light/dark
      toggleTheme: () => set((state) => ({
        theme: state.theme === 'light' ? 'dark' : 'light',
      })),
    }),
    {
      name: 'theme-storage', // 本地存储的 key(localStorage 中显示为 `zustand-theme-storage`)
      getStorage: () => localStorage, // 存储方式:localStorage(默认)或 sessionStorage
    }
  )
);

// 组件中使用:刷新页面后,theme 状态会从 localStorage 恢复
const ThemeToggle = () => {
  const { theme, toggleTheme } = useThemeStore();
  return <button onClick={toggleTheme}>切换{theme === 'light' ? '深色' : '浅色'}主题</button>;
};

5. 调试:集成 Redux DevTools

Zustand 支持接入 Redux DevTools,实现状态回溯、Action 日志等调试功能:

typescript 复制代码
import { create } from 'zustand';
import { devtools } from 'zustand/middleware'; // 导入调试中间件

// 创建 Store 时包裹 devtools 中间件
const useCounterStore = create(
  devtools((set) => ({
    count: 0,
    increment: () => set((state) => ({ count: state.count + 1 })),
  }), {
    name: 'counter-store', // DevTools 中显示的 Store 名称
  })
);
启用调试:
  1. 安装 Chrome 插件「Redux DevTools」。
  2. 打开浏览器开发者工具 → Redux 面板,即可看到 counter-store 的状态变更日志,支持时间旅行调试。

五、实战示例:电商购物车

整合上述特性,实现一个完整的电商购物车 Store:

typescript 复制代码
// stores/cartStore.ts
import { create } from 'zustand';
import { persist } from 'zustand-persist';
import { devtools } from 'zustand/middleware';
import { shallow } from 'zustand/shallow';

// 商品类型定义(TS)
interface CartItem {
  id: number;
  name: string;
  price: number;
  quantity: number;
  img: string;
}

// 创建 Store:结合持久化 + 调试中间件
const useCartStore = create(
  devtools(
    persist(
      (set, get) => ({
        // 状态
        cartItems: [] as CartItem[],

        // 计算属性:购物车商品总数
        getTotalCount: () => {
          return get().cartItems.reduce((total, item) => total + item.quantity, 0);
        },

        // 计算属性:购物车总价
        getTotalPrice: () => {
          return get().cartItems.reduce((total, item) => total + item.price * item.quantity, 0);
        },

        // 方法:添加商品
        addToCart: (item: Omit<CartItem, 'quantity'>) => {
          const existingItem = get().cartItems.find((i) => i.id === item.id);
          if (existingItem) {
            // 商品已存在:更新数量
            set((state) => ({
              cartItems: state.cartItems.map((i) =>
                i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i
              ),
            }), false, 'cart/add'); // 第三个参数:Action 名称(调试用)
          } else {
            // 商品不存在:添加新商品
            set((state) => ({
              cartItems: [...state.cartItems, { ...item, quantity: 1 }],
            }), false, 'cart/add');
          }
        },

        // 方法:更新数量
        updateQuantity: (id: number, quantity: number) => {
          set((state) => ({
            cartItems: state.cartItems.map((i) =>
              i.id === id ? { ...i, quantity: Math.max(1, quantity) } : i // 数量至少为 1
            ),
          }), false, 'cart/update');
        },

        // 方法:删除商品
        removeItem: (id: number) => {
          set((state) => ({
            cartItems: state.cartItems.filter((i) => i.id !== id),
          }), false, 'cart/remove');
        },

        // 方法:清空购物车
        clearCart: () => {
          set({ cartItems: [] }, false, 'cart/clear');
        },
      }),
      {
        name: 'cart-storage', // 本地存储 key
        getStorage: () => localStorage,
      }
    )
  )
);

// 导出简化的 Hooks(可选,方便组件使用)
export const useCartItems = () => useCartStore((state) => state.cartItems);
export const useCartActions = () =>
  useCartStore(
    (state) => ({
      addToCart: state.addToCart,
      updateQuantity: state.updateQuantity,
      removeItem: state.removeItem,
      clearCart: state.clearCart,
    }),
    shallow
  );
export const useCartSummary = () =>
  useCartStore(
    (state) => ({
      totalCount: state.getTotalCount(),
      totalPrice: state.getTotalPrice(),
    }),
    shallow
  );

export default useCartStore;
组件中使用:
tsx 复制代码
// CartPage.tsx
import { useCartItems, useCartActions, useCartSummary } from '@/stores/cartStore';

const CartPage = () => {
  const cartItems = useCartItems();
  const { addToCart, updateQuantity, removeItem, clearCart } = useCartActions();
  const { totalCount, totalPrice } = useCartSummary();

  // 示例:添加商品
  const handleAddTestItem = () => {
    addToCart({
      id: Date.now(),
      name: '测试商品',
      price: 99,
      img: 'https://picsum.photos/200',
    });
  };

  if (cartItems.length === 0) {
    return (
      <div>
        <h1>购物车为空</h1>
        <button onClick={handleAddTestItem}>添加测试商品</button>
      </div>
    );
  }

  return (
    <div>
      <h1>购物车({totalCount} 件商品)</h1>
      <button onClick={clearCart}>清空购物车</button>
      <ul>
        {cartItems.map((item) => (
          <li key={item.id}>
            <img src={item.img} alt={item.name} style={{ width: '50px' }} />
            <div>{item.name}</div>
            <div>¥{item.price}</div>
            <button onClick={() => updateQuantity(item.id, item.quantity - 1)}>-</button>
            <span>{item.quantity}</span>
            <button onClick={() => updateQuantity(item.id, item.quantity + 1)}>+</button>
            <button onClick={() => removeItem(item.id)}>删除</button>
          </li>
        ))}
      </ul>
      <div>总价:¥{totalPrice.toFixed(2)}</div>
    </div>
  );
};

export default CartPage;

六、常见问题与注意事项

1. 避免闭包陷阱

组件中直接缓存 Store 方法可能导致闭包陷阱(获取到旧状态),解决方案:

  • 不缓存方法,直接通过 useCartStore 获取(推荐)。

  • 若需缓存,使用 useCallback 并依赖 Store 方法:

    tsx 复制代码
    const { addToCart } = useCartActions();
    const handleAdd = useCallback(() => {
      addToCart({ id: 1, name: '商品', price: 100, img: '' });
    }, [addToCart]); // 依赖 addToCart(确保方法是最新的)

2. 状态不可变性(可选)

Zustand 支持 mutable 模式(直接修改状态),但推荐保持不可变性(便于调试和时间旅行):

typescript 复制代码
// 不推荐(mutable,虽然能工作,但调试不便)
const useCounterStore = create((set) => ({
  count: 0,
  increment: () => set((state) => {
    state.count += 1; // 直接修改状态(mutable)
    return state;
  }),
}));

// 推荐(immutable,返回新状态)
const useCounterStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}));

3. 组件卸载时取消订阅(无需手动处理)

Zustand 的 useStore 会自动在组件卸载时取消订阅,无需手动清理,避免内存泄漏。

七、总结

Zustand 入门门槛极低,核心 API 仅 2 个,却能覆盖绝大多数前端状态管理场景:

  • 简单场景:直接定义状态和方法,组件中订阅使用。
  • 复杂场景:通过中间件(持久化、调试)、浅比较、异步方法扩展。

核心优势:极简 API、零模板代码、高性能、TS 友好、无需 Provider。

如果你的项目是 React 技术栈,且需要一个「轻量、高效、易上手」的状态管理库,Zustand 绝对是首选!

相关推荐
im_AMBER21 小时前
Canvas架构手记 07 状态管理 | 组件通信 | 控制反转
前端·笔记·学习·架构·前端框架·react
闲人编程1 天前
Django与前端框架集成:Vue.js、React的完美配合
vue.js·django·前端框架·react·codecapsule·分离式部署
Tongfront1 天前
前端通用submit方法
开发语言·前端·javascript·react
在未来等你4 天前
AI Agent设计模式 Day 19:Feedback-Loop模式:反馈循环与自我优化
设计模式·llm·react·ai agent·plan-and-execute
A3608_(韦煜粮)5 天前
深入理解React Hooks设计哲学与实现原理:从闭包陷阱到并发模式
javascript·性能优化·react·前端开发·react hooks·并发模式·自定义hooks
safestar20127 天前
React 性能优化之Fiber 架构深度解析:从堆栈调和到增量渲染的革命
前端·javascript·react
aiguangyuan9 天前
React 18 源码解读(一)
javascript·react·前端开发
aiguangyuan9 天前
React 中什么是可中断更新?
javascript·react·前端开发
人工智能训练10 天前
前端框架选型破局指南:Vue、React、Next.js 从差异到落地全解析
运维·javascript·人工智能·前端框架·vue·react·next.js