探索学习 Zustand 源码实现🔍🔍🔍

引言

在上一篇《探索 Antd Form 表单实现原理》中,我们深入剖析了表单状态管理的核心机制,文末提到了第三方状态管理库的设计思想。这些思想与 Redux、Zustand 等主流状态管理库,以及 React 18 中新引入的 useSyncExternalStore 钩子可谓异曲同工,都围绕着同一个核心问题:如何优雅地在 React 组件树中共享和同步状态?

React 18 的 useSyncExternalStore 的推出,标志着 React 官方对第三方状态管理库的正式认可和标准化。这个看似简单的 Hook,实际上解决了并发渲染下外部状态同步的复杂问题,为像 Zustand 这样的状态管理库提供了坚实的基础。

今天,我们将深入 Zustand 的源码实现,探究它是如何基于 feat: use-sync-external-store 构建出一个既简洁又强大的状态管理方案。从发布订阅模式的经典实现,到 React 18 并发特性的完美适配。

基本使用

在正式深入源码解析之前,我们先来感受一下 Zustand 的魅力。官方提供了一个非常精美的 交互式演示,通过这个生动的 demo,你可以直观地体验 Zustand 的核心特性和使用场景。

推荐学习路径:

🎯 第一步:体验实战 - 通过 官方演示 感受 Zustand 的简洁优雅

🔍 第二步:掌握基础 - 阅读 GitHub README 了解核心 API 和最佳实践

📚 第三步:深入学习 - 查阅 官方文档 探索高级用法和设计理念

这种由浅入深的学习方式,能够帮你快速掌握 Zustand 的基本使用,为接下来的源码探索打下坚实基础。毕竟,只有真正理解了 API 的设计意图,才能更好地理解其底层实现。

基础

首先我们要知道zustand 提供了什么能力。官方 GitHub README 下面提到了两种使用方式:

在 react 中使用:

js 复制代码
import { create } from 'zustand'


const useBearStore = create((set) => ({
  bears: 0,
  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
  removeAllBears: () => set({ bears: 0 }),
}))


function BearCounter() {
  const bears = useBearStore((state) => state.bears)
  return <h1>{bears} around here ...</h1>
}


function Controls() {
  const increasePopulation = useBearStore((state) => state.increasePopulation)
  return <button onClick={increasePopulation}>one up</button>
}

在原生 js 中使用

js 复制代码
import { createStore } from 'zustand/vanilla'


const store = createStore((set) => ...)
const { getState, setState, subscribe, getInitialState } = store


export default store

Zustand 源码

Zustand 的源码是用 TypeScript 编写的,但类型定义可能会影响代码的阅读体验。对于这类 TypeScript 项目,我们可以选择阅读打包后的代码(TypeScript 编译器会将 .ts 文件编译为 .js 文件和对应的 .d.ts 类型声明文件)。

核心逻辑层

Zustand 的核心逻辑层(vanilla)实现了纯粹的状态管理机制,完全独立于任何前端框架。让我们深入探索这个精心设计的核心实现。

完整源码

js 复制代码
// 打包后的 vanilla.js
"use strict";


const createStoreImpl = (createState) => {
  let state;
  const listeners = /* @__PURE__ */ new Set();
  const setState = (partial, replace) => {
    const nextState = typeof partial === "function" ? partial(state) : partial;
    if (!Object.is(nextState, state)) {
      const previousState = state;
      state = (
        replace != null
          ? replace
          : typeof nextState !== "object" || nextState === null
      )
        ? nextState
        : Object.assign({}, state, nextState);
      listeners.forEach((listener) => listener(state, previousState));
    }
  };
  const getState = () => state;
  const getInitialState = () => initialState;
  const subscribe = (listener) => {
    listeners.add(listener);
    return () => listeners.delete(listener);
  };
  const api = { setState, getState, getInitialState, subscribe };
  const initialState = (state = createState(setState, getState, api));
  return api;
};
const createStore = (createState) =>
  createState ? createStoreImpl(createState) : createStoreImpl;


exports.createStore = createStore;

核心设计思路

如果我是 Zustand 的作者,我首先会问自己:状态管理的本质究竟是什么?

剥离掉 React、Vue 这些框架的外衣,状态管理其实就是三件事:

  1. 存储状态 - 在内存中维护数据
  2. 更新状态 - 提供修改数据的机制
  3. 通知变化 - 让关心状态的地方知道变化
js 复制代码
// 这就是状态管理的本质抽象
const createStoreImpl = (createState) => {
  let state;                           // 存储:闭包变量保存状态
  const listeners = new Set();         // 通知:订阅者管理
  const setState = (partial) => { ... } // 更新:状态修改接口
  const subscribe = (listener) => { ... } // 订阅:变化监听机制
}
js 复制代码
const listeners = /* @__PURE__ */ new Set();

为什么用 Set 而不是数组?

作为作者,我考虑过重复订阅的边界情况。如果一个组件因为某种原因多次订阅了同一个状态,用数组可能会导致重复通知,影响性能甚至引发 bug。而 Set 天然具备去重特性,优雅地解决了这个问题。

注释 /* @__PURE__ */ 告诉打包工具这是无副作用的操作,便于 tree-shaking 优化。

❗状态更新机制

js 复制代码
const setState = (partial, replace) => {

这个函数签名体现了作者对 API 设计的深度思考:

  • partial:语义上暗示"部分更新",这是大多数场景的需求
  • replace:处理完全替换的特殊需求,如状态重置或服务器数据同步
js 复制代码
const nextState = typeof partial === "function" ? partial(state) : partial;

这一行代码巧妙地统一了两种更新模式:

js 复制代码
// 模式1:直接传值 - 简单直观
setState({ count: 5 })


// 模式2:函数式更新 - 解决闭包陷阱
setState(state => ({ count: state.count + 1 }))

为什么要支持函数式更新?

这是借鉴了 React setState 的经验教训。在异步操作或连续更新中,直接使用变量值容易产生闭包陷阱:

js 复制代码
// 🚫 危险:可能基于过期的状态
setTimeout(() => {
  setState({ count: currentCount + 1 }); // currentCount 可能已经过期
}, 1000);


// ✅ 安全:总是基于最新状态
setTimeout(() => {
  setState(state => ({ count: state.count + 1 })); // state 永远是最新的
}, 1000);

变化检测

js 复制代码
if (!Object.is(nextState, state)) {

作者选择 Object.is() 而非简单的 === 比较,这体现了对边界情况的细致考虑:

js 复制代码
// 边界情况对比
Object.is(NaN, NaN)        // true  - NaN 被认为相同,不触发更新
NaN === NaN                // false


Object.is(+0, -0)          // false - 正负零被认为不同,会触发更新  
+0 === -0                  // true

历史状态的保存

js 复制代码
const previousState = state;
  • 订阅者需求:许多场景需要对比新旧状态
  • 调试支持:开发工具可以展示状态变迁历史

状态更新策略的分层逻辑

js 复制代码
state = (
  replace != null
    ? replace
    : typeof nextState !== "object" || nextState === null
)
  ? nextState
  : Object.assign({}, state, nextState);

这个看似复杂的三元表达式,实际上体现了清晰的优先级逻辑:

js 复制代码
// 优先级解析
if (replace != null) {
  // 1. 显式替换模式 - 最高优先级
  state = replace;
} else if (typeof nextState !== "object" || nextState === null) {
  // 2. 基本类型自动替换 - 无法合并的情况
  state = nextState;
} else {
  // 3. 对象合并模式 - 默认行为
  state = Object.assign({}, state, nextState);
}

设计场景覆盖:

js 复制代码
// 场景1:显式替换
setState({ count: 1 }, true)     // 完全替换,忽略合并


// 场景2:基本类型替换  
setState(42)                     // 状态变成数字 42
setState("hello")                // 状态变成字符串 "hello"
setState(null)                   // 状态重置为 null


// 场景3:对象合并(最常用)
setState({ count: 1 })           // 与现有状态智能合并

发布订阅的核心实现

js 复制代码
listeners.forEach((listener) => listener(state, previousState));

这行代码是整个状态管理系统的神经中枢,负责将状态变化传播给所有关心的订阅者。

为什么传递两个参数?

js 复制代码
listener(newState, oldState)

这种设计考虑了实际应用场景:

js 复制代码
// 典型使用场景:状态对比分析
store.subscribe((newState, oldState) => {
  // 用户切换检测
  if (newState.user.id !== oldState.user.id) {
    analytics.track('user_changed');
  }
  
  // 购物车变化检测
  if (newState.cart.items.length > oldState.cart.items.length) {
    showNotification('商品已添加到购物车');
  }
});

❗️初始化函数

让我们来看这行看似简单但设计精妙的代码:

js 复制代码
const initialState = (state = createState(setState, getState, api));

它等价于:

js 复制代码
// 步骤1:执行用户函数并赋值给 state
state = createState(setState, getState, api);


// 步骤2:将赋值后的 state 保存为 initialState
const initialState = state;
理解执行流程:有无初始化函数的区别

这里的 createState 是用户传入的初始化函数,而不是 Zustand 内部定义的。为了更好地理解这个设计,让我们通过对比来看看它解决了什么问题。

方案A:不提供初始化函数(传统方式)

如果 Zustand 不要求传入 createState,我们只能这样使用:

js 复制代码
// 创建一个空的 store
const store = createStore();


// 手动设置初始状态
store.setState({ count: 0 });


// 每次操作都需要手动写状态更新逻辑
store.setState({ count: store.getState().count + 1 }); // 增加计数
store.setState({ count: store.getState().count - 1 }); // 减少计数


// 问题:
// 1. 业务逻辑分散,难以维护
// 2. 重复的状态更新代码
// 3. 容易出现闭包陷阱
// 4. 没有封装,每次都要手写逻辑

方案B:提供初始化函数

通过 createState 函数,我们可以优雅地定义状态和操作:

js 复制代码
// 用户定义状态结构和操作逻辑
const store = createStore((set, get, api) => ({
  // 🎯 声明式的初始状态
  count: 0,
  user: null,
  
  // 🎯 封装好的操作方法,避免重复代码
  increment: () => set(state => ({ count: state.count + 1 })),
  decrement: () => set(state => ({ count: state.count - 1 })),
  setUser: (user) => set({ user }),
  
  // 🎯 计算属性和复杂逻辑
  getDoubleCount: () => get().count * 2,
  reset: () => set({ count: 0, user: null })
}));


// 使用变得简洁而直观
store.increment();        // 直接调用方法
store.decrement();        // 业务语义清晰
store.reset();           // 封装的复杂操作

为什么要这样设计?

这种设计模式叫做依赖注入:

  • 控制反转:Zustand 把状态初始化的控制权交给用户
  • 能力注入:用户的初始化函数可以使用 Zustand 提供的状态管理能力
  • 闭包封装:用户定义的方法可以通过闭包访问到 set 和 get

框架适配层

Zustand 框架适配层的设计精妙,它充分展现了"分离关注点"的设计哲学。核心逻辑层专注于纯粹的状态管理,而框架适配层则负责将这些能力无缝集成到 React 生态中。

完整源码

js 复制代码
// 打包后的 react.js
"use strict";


var React = require("react");
var vanilla = require("zustand/vanilla");


const identity = (arg) => arg;
function useStore(api, selector = identity) {
  const slice = React.useSyncExternalStore(
    api.subscribe,
    () => selector(api.getState()),
    () => selector(api.getInitialState())
  );
  return slice;
}
const createImpl = (createState) => {
  const api = vanilla.createStore(createState);
  const useBoundStore = (selector) => useStore(api, selector);
  Object.assign(useBoundStore, api);
  return useBoundStore;
};
const create = (createState) =>
  createState ? createImpl(createState) : createImpl;


exports.create = create;
exports.useStore = useStore;

创建入口

js 复制代码
const create = (createState) =>
  createState ? createImpl(createState) : createImpl;
  
// 直接调用
const useStore = create((set) => ({ count: 0 }));


// 分步调用
const createStore = create();
const useStore = createStore((set) => ({ count: 0 }));

一个柯里化操作,用于初始化状态仓库,支持直接调用和分步调用两种方式。

createImpl:API 统一设计

js 复制代码
const createImpl = (createState) => {
  const api = vanilla.createStore(createState);
  const useBoundStore = (selector) => useStore(api, selector);
  Object.assign(useBoundStore, api);
  return useBoundStore;
};

createImpl 是整个适配层的核心实现,它完成了三个关键任务:

  1. 调用核心层:通过 vanilla.createStore(createState) 创建核心逻辑层的仓库实例,获得完整的 API 对象
  2. 创建 Hook 函数:构建 useBoundStore 函数,它接受 selector 参数并调用 useStore 获取状态切片
  3. API 能力扩展:使用 Object.assign 将原始仓库的所有方法暴露到 Hook 函数上

这里的关键在于理解 JavaScript 中函数本质上就是对象。这意味着我们可以像操作普通对象一样给函数添加属性和方法。

返回的 useBoundStore 既是一个 React Hook,又具备完整的仓库操作能力:

js 复制代码
// 作为 Hook 使用,获取状态切片
function BearCounter() {
  const bears = useBearStore((state) => state.bears);
  return <h1>{bears} around here ...</h1>;
}


function Controls() {
  const increasePopulation = useBearStore((state) => state.increasePopulation);
  return <button onClick={increasePopulation}>one up</button>;
}

组件外操作能力

用户可以直接在组件外部操作全局状态:

js 复制代码
useBoundStore.getState();
useBoundStore.setState({ ... });
useBoundStore.subscribe(() => {});

这种设计带来了强大的能力,可被用于事件处理与副作用管理等场景:

js 复制代码
// 在事件处理器、API 调用、定时器等场景中直接操作状态
function handleGlobalEvent() {
  const currentState = useBearStore.getState();
  console.log('当前熊的数量:', currentState.bears);
  
  // 直接更新状态,无需在组件内部
  useBearStore.setState({ bears: currentState.bears + 10 });
}


// 全局状态监听与分析
useBearStore.subscribe((state, prevState) => {
  if (state.bears !== prevState.bears) {
    // 执行分析、日志记录等副作用
    analytics.track('bears_count_changed', { 
      from: prevState.bears, 
      to: state.bears 
    });
  }
});


// 异步操作中的状态管理
async function fetchAndUpdateBears() {
  try {
    const response = await fetch('/api/bears');
    const data = await response.json();
    useBearStore.setState({ bears: data.count });
  } catch (error) {
    console.error('获取熊数量失败:', error);
  }
}

useStore:React 生态的完美桥梁

React 18 推出了一个专门为外部状态管理而生的 Hook: useSyncExternalStore

js 复制代码
function useStore(api, selector = identity) {
  const slice = React.useSyncExternalStore(
    api.subscribe,
    () => selector(api.getState()),
    () => selector(api.getInitialState())
  );
  return slice;
}
参数
  • subscribe:一个函数,接收一个单独的 callback 参数并把它订阅到 store 上。当 store 发生改变时会调用提供的 callback,这将导致 React 重新调用 getSnapshot 并在需要的时候重新渲染组件。subscribe 函数会返回清除订阅的函数。
  • getSnapshot:一个函数,返回组件需要的 store 中的数据快照。在 store 不变的情况下,重复调用 getSnapshot 必须返回同一个值。如果 store 改变,并且返回值也不同了(用 Object.is 比较,React 就会重新渲染组件。
  • 可选 getServerSnapshot:一个函数,返回 store 中数据的初始快照。它只会在服务端渲染时,以及在客户端进行服务端渲染内容的激活时被用到。快照在服务端与客户端之间必须相同,它通常是从服务端序列化并传到客户端的。如果你忽略此参数,在服务端渲染这个组件会抛出一个错误。
返回值

该 store 的当前快照,可以在你的渲染逻辑中使用。

这三个参数是否看起来很眼熟?它们正是核心逻辑层中 createStoreImpl 返回的 api 对象的核心属性!通过这样的参数传递,React 就能自动帮我们处理状态变化时的重新渲染逻辑。

中间件

中间件(Middleware)是 Zustand 实现功能扩展的核心机制,它允许我们在不修改核心代码的前提下,为状态管理注入额外的能力。官方 GitHub README 下面提到了两种中间件,我们分别来学习一下。

immer

如果你用过 React 开发真实项目,一定不会对 immer 陌生。在传统的状态管理中,我们必须严格遵循不可变更新原则

js 复制代码
// 🚫 危险:直接修改状态对象
setState(state => {
  state.bees += 5;  // 违反不可变性
  return state;
});


// ✅ 正确但繁琐:手动创建新对象
setState(state => ({
  ...state,
  bees: state.bees + 5
}));

对于深层嵌套的状态结构,这种写法既冗长又容易出错。Zustand 官方也考虑到了这一点,并为我们提供了两种集成 Immer 的方式:

方式二:直接使用 produce 函数

js 复制代码
import { produce } from 'immer'


const useLushStore = create((set, get, store) => ({
  lush: { forest: { contains: { a: 'bear' } } },
  clearForest: () =>
    set(
      produce((state) => {
        state.lush.forest.contains = null
      }),
    ),
}))


const clearForest = useLushStore((state) => state.clearForest)
clearForest()

方式二:使用 immer 中间件(推荐)

js 复制代码
import { create } from 'zustand'
import { immer } from 'zustand/middleware/immer'


const useBeeStore = create(
  immer((set, get, store) => ({
    bees: 0,
    addBees: (by) =>
      set((state) => {
        state.bees += by
      }),
  })),
)

immer 中间件源码解析

sequenceDiagram participant User as 用户代码 participant React as React 框架层 participant Core as 核心层 participant Immer as Immer 中间件 participant Original as 用户 initializer User->>React: create(immer(initializer)) React->>Core: vanilla.createStore(immer(initializer)) Core->>Core: 创建原始 setState, getState, api Note over Core: setState = 原始状态更新逻辑
getState = 获取当前状态
api = {setState, getState, subscribe} Core->>Immer: 调用 immer(initializer)(setState, getState, api) Note over Immer: 接收原始能力参数 Immer->>Immer: 增强 api.setState = immer版本 Note over Immer: 用 produce 包装函数式更新 Immer->>Original: 调用 initializer(增强的setState, getState, api) Note over Original: 用户代码接收到增强能力 Original->>Immer: 返回状态和方法 Immer->>Core: 返回初始状态 Core->>React: 返回完整 api React->>User: 返回 useBoundStore Hook

先贴一张详细的流程图,大家阅读代码困难的时候可以从图中找找灵感。

源码文件
js 复制代码
// 打包后的 immer.js
'use strict';


var immer$1 = require('immer');


const immerImpl = (initializer) => (set, get, store) => {
  store.setState = (updater, replace, ...args) => {
    const nextState = typeof updater === "function" ? immer$1.produce(updater) : updater;
    return set(nextState, replace, ...args);
  };
  return initializer(store.setState, get, store);
};
const immer = immerImpl;


exports.immer = immer;

这里的参数对应核心逻辑层中的关键调用,也是用户调用 create 时入参的函数的参数:

js 复制代码
// 核心层 vanilla.js
const createStoreImpl = (createState) => {
  const setState = (partial, replace) => { /* 原始状态更新逻辑 */ };
  const getState = () => state;
  const api = { setState, getState, getInitialState, subscribe };
  
  // 🎯 关键调用:依赖注入
  const initialState = (state = createState(setState, getState, api));
  //                              ^^^^^^^^  ^^^^^^^^  ^^^
  //                              对应中间件的 set, get, store 参数
  return api;
};

设计精髓:immer 中间件本质上是一个依赖注入容器,它接收核心层提供的原始能力,进行增强后再提供给用户。

类型判断
js 复制代码
typeof updater === "function" ? immer$1.produce(updater) : updater


// 函数式更新:自动使用 immer 处理,支持"可变"语法
set((state) => {
  state.bees += 10; 
});


// 对象式更新:直接透传,保持原有行为
set({ bees: 5 });  
函数包装与链式组合
js 复制代码
const immerImpl = (initializer) => (set, get, store) => {
  
  store.setState = (updater, replace, ...args) => {
    // ↑ 包装函数:添加 immer 处理逻辑
    const nextState = typeof updater === "function" ? produce(updater) : updater;
    return set(nextState, replace, ...args);  // 调用传入的 set 参数
    //     ^^^ 这里的 set 是核心层提供的原始 setState(单中间件时),或者是上一个中间件增强过的版本(多中间件时)
  };
  
  // 传递增强后的 setState 给下一层
  return initializer(store.setState, get, store);
};

上面笔者画的那个流程图只考虑了一个中间件的情况,所以图中说的是调用原始的 setState 方法,但实际上如果是链式调用,调用的是上一个中间件增强过的 set 方法。

核心层只调用一次 createState,而这个 createState 实际上是整个中间件链预组合的结果。也就是说,最终传递给 create 函数的初始化函数,已经是经过所有中间件层层包装后的产物。

persist

状态持久化是一个常见且重要的需求,比如用户不希望刷新页面后丢失购物车内容,也不希望重新登录后需要重新配置个人偏好。Zustand 提供的 persist 中间件的使用方式相当直观:

js 复制代码
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'


const useFishStore = create(
  persist(
    (set, get) => ({
      fishes: 0,
      addAFish: () => set({ fishes: get().fishes + 1 }),
    }),
    {
      name: 'food-storage', // name of the item in the storage (must be unique)
      storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used
    },
  ),
)

createJSONStorage 存储抽象层

createJSONStoragepersist 的核心依赖,我们先来介绍一下它:

js 复制代码
function createJSONStorage(getStorage, options) {
  let storage;
  try {
    storage = getStorage();
  } catch (e) {
    return;
  }
  const persistStorage = {
    getItem: (name) => {
      var _a;
      const parse = (str2) => {
        if (str2 === null) {
          return null;
        }
        return JSON.parse(str2, options == null ? void 0 : options.reviver);
      };
      const str = (_a = storage.getItem(name)) != null ? _a : null;
      if (str instanceof Promise) {
        return str.then(parse);
      }
      return parse(str);
    },
    setItem: (name, newValue) => storage.setItem(name, JSON.stringify(newValue, options == null ? void 0 : options.replacer)),
    removeItem: (name) => storage.removeItem(name)
  };
  return persistStorage;
}

该方法整体比较简单,就是通过包装函数增强了原生 storage 的能力,就不逐行解释了。它采用了适配器模式,具体作用作用如下:

  1. 统一接口:不管底层是 localStorage、sessionStorage、asyncStorage 还是自定义存储,都提供相同的 API
  2. 增强功能:在原生存储 API 基础上添加 JSON 序列化/反序列化能力
  3. 异步兼容:智能处理同步和异步存储,对上层透明
  4. 错误处理:优雅处理存储不可用的情况(getStorage 不是直接传入存储对象,而是传入获取存储对象的函数,这样只会在调用的时候才报错,而不是页面执行的时候直接报错)

可选参数 options 中的 replacer 可以用于过滤敏感字段、自定义序列化逻辑(如 Date 对象处理),reviver 用于将其还原。

persist 源码解析

persist 的源码比较长,这里笔者提取了最核心的持久化存储逻辑代码,去除了水合、版本迁移等复杂逻辑,方便大家阅读学习:

js 复制代码
const persistImpl = (config, baseOptions) => (set, get, api) => {
  /*
  * 这部分建立了 persist 中间件的配置体系。默认使用 localStorage 作为存储,partialize 函数默认保存完整状态,版本号从 0 开始。用户传入的 baseOptions 会覆盖这些默认值。
  */
  let options = {
    storage: createJSONStorage(() => localStorage),
    partialize: (state) => state,
    version: 0,
    ...baseOptions
  };


  let storage = options.storage;


  /*
  * 当存储不可用时(如浏览器隐私模式、存储配额满等),中间件不会阻塞应用运行,而是退化为普通的状态管理,只是失去了持久化能力。
  */
  if (!storage) {
    return config(
      (...args) => {
        console.warn(
          `[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.`
        );
        set(...args);
      },
      get,
      api
    );
  }


  /*
  * 这部分实现了持久化逻辑。
  * 1. 获取当前状态
  * 2. 使用 partialize 函数处理状态,只保存部分状态
  * 3. 将状态和版本号打包存储
  */
  const setItem = () => {
    const currentState = { ...get() };
    const stateToSave = options.partialize(currentState);
    const dataToStore = {
      state: stateToSave,
      version: options.version
    };
    return storage.setItem(options.name, dataToStore);
  };


  /*
  * 这部分实现了状态更新时的持久化逻辑。
  * 1. 获取原始的 setState 函数
  * 2. 重写 setState 函数,在更新状态后自动调用 setItem
  */
  const originalSetState = api.setState;
  api.setState = (state, replace) => {
    originalSetState(state, replace);
    void setItem();
  };


  const configResult = config(
    (...args) => {      // ← 这是增强版的 set 函数
      set(...args);     // ← 调用原始的 set(来自 vanilla 核心层)
      void setItem();   // ← 自动保存到存储(void 确保不返回值)
    },
    get,               // ← 原始的 get 函数
    api                // ← 原始的 api 对象
  );


  /*
  * 这部分实现了 persist 对象,提供了清除存储、获取配置和设置配置的功能。
  */
  api.persist = {
    clearStorage: () => {
      storage?.removeItem(options.name);
    },
    getOptions: () => options,
    setOptions: (newOptions) => {
      options = {
        ...options,
        ...newOptions
      };
      if (newOptions.storage) {
        storage = newOptions.storage;
      }
    }
  };


  return configResult;
};

可以看出,persist 中间件的本质,就是通过代理模式拦截所有的状态更新操作,在状态发生变化时,自动向存储(如 localStorage)中保存一份状态快照。

总结

深入 Zustand 源码,看到一个优秀状态管理库的典范:用极简实现高效。相比 Redux 的 Action/Reducer 模板和 Provider 包裹,Zustand 直接修改状态、免 Provider 接入 React,并自动优化组件重渲染。从核心的发布订阅,到 React 的丝滑适配,再到中间件的灵活扩展,每一层都恰到好处。Zustand 的强大不在于复杂,而在于它用简洁优雅的方式解决了状态管理的核心问题------当你追求更少的样板、更直接的 API 和开箱即用的性能时,它就是上佳之选。

本来还想深入解析一下 useSyncExternalStore 的内部实现机制,但发现它涉及到 React 18 并发渲染、Fiber 架构等更深层的源码逻辑,这些内容足够单独写一篇文章了,就先在这里挖个坑,等我把 React 源码啃完再来填坑。

相关推荐
anyup4 分钟前
AI 也救不了的前端坑,你遇到过吗?社区、AI、源码三重排查!
前端·数据可视化·cursor
tager11 分钟前
还在为跨框架的微信表情包烦恼?我写了个通用的,拿去吧!🚀
前端·vue.js·react.js
陈随易16 分钟前
一段时间没写文章了,花了10天放了个屁
前端·后端·程序员
Codebee22 分钟前
OneCode基础组件介绍——树形组件(Tree)
前端·编程语言
Cheishire_Cat22 分钟前
AI Coding宝藏组合:Cursor + Cloudbase-AI-Toolkit 开发游戏实战
前端
audience33 分钟前
uni-app运行环境版本和编译器版本不一致的问题
前端
零者35 分钟前
深度解析:React Native Android 上“调试JS”按钮失效的背后原因与修复
前端
前端付豪35 分钟前
Google Ads 广告系统排序与实时竞价架构揭秘
前端·后端·架构
邢行行35 分钟前
NPM 核心知识点:一份清晰易懂的复习指南
前端
颜漠笑年35 分钟前
看看DeepSeek是如何实现前端日历组件的?
前端·html·代码规范