探索学习 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 源码啃完再来填坑。

相关推荐
玲小珑21 分钟前
Next.js 教程系列(十六)Next.js 中的状态管理方案
前端·next.js
前端小巷子23 分钟前
web实现文件的断点续传
前端·javascript·面试
小磊哥er23 分钟前
【前端工程化】前端项目怎么做代码管理才好?
前端
jojo是只猫1 小时前
前端vue对接海康摄像头流程
前端·javascript·vue.js
10年前端老司机5 小时前
React无限级菜单:一个项目带你突破技术瓶颈
前端·javascript·react.js
阿芯爱编程9 小时前
2025前端面试题
前端·面试
前端小趴菜0510 小时前
React - createPortal
前端·vue.js·react.js
晓131311 小时前
JavaScript加强篇——第四章 日期对象与DOM节点(基础)
开发语言·前端·javascript
菜包eo11 小时前
如何设置直播间的观看门槛,让直播间安全有效地运行?
前端·安全·音视频
烛阴12 小时前
JavaScript函数参数完全指南:从基础到高级技巧,一网打尽!
前端·javascript