阅读 zustand.js

前言

最近在看一些状态管理的库,zustand 目前是社区比较流行的选择,所以先阅读下它的源码,来理解它是如何解决前端状态管理的问题的。

  • 最先应该是问题
  1. 为什么它性能会好一些?
  2. 为什么它的侵入性比较低?
  3. 它的生态怎么样?是否有商用的案例?
  • 为了解决这些问题,它设计了哪些 API,主要会产生什么能力

根据问题来依次解决

1. 为什么它性能会好一些?

首先它能够基于 Selector 进行精准更新,所以相对 Context-Provider 之类的管理模式,避免了 Context 范围内的大量更新,以及 Context 订阅失效的一些问题,性能自然会好一些。但是 mobx 等依然也能精准更新,那么它的优势是什么呢?

看完源码后其实它享受了 React 的福利,那就是 useSyncExternalStore 接入三方 Store 绑定 Fiber State 的能力,这样让 zustand 的结构相当简单,库也很好,没有 mobx 那么多依赖复杂。zustand 更像是一个更好的 Redux 模型升级。

2. 为什么它的侵入性比较低?

概要结构

因为它的实现其实很简单,我们先来看下它的源码 src 目录和 API 设计自然就了解。其中 vanilla.ts 是核心的功能实现,src/react.ts 是连接 react 能力的核心实现。

bash 复制代码
├── context.ts
├── index.ts
├── middleware
│   ├── combine.ts
│   ├── devtools.ts
│   ├── immer.ts
│   ├── persist.ts
│   ├── redux.ts
│   └── subscribeWithSelector.ts
├── middleware.ts
├── react
│   └── shallow.ts
├── react.ts
├── shallow.ts
├── traditional.ts
├── types.d.ts
├── vanilla
│   └── shallow.ts
└── vanilla.ts

整体 zustand 的 API 包含以下内容,除去扩展能力,核心能力比较少。

源码设计

源码部分,基本就是一个 Redux 基础模型的实现,主要有这么几个:以下是伪代码

javascript 复制代码
// 1. setState 的时候,批量支持订阅的监听器
setState() {
    if (Object.is(nextState, state)) return;
    
    // 存下 prev
    const prevState = state;
    
    // 开始产生新 state
    state = replace ? nextState : assign({}, state, nextState);
    
    // 执行监听器
    each(listeners, (listener) => listener(state, prevState))
}

// 2. getState 就是简单获取
getState() {
    return state;
}

// 3. getInitialState 拥有动态的能力
getInitialState -> state -> createState(setState, getState, api);

// 4. subscribe 就是一个监听数组操作
subscribe(listener) {
    listeners.add(listener);
    
    return () => listeners.delete(listener);
}

实现这个之后,zustand 基本能力的组合就到了 react.ts,核心就是使用 React.useSyncExternalStoreWithSelector,所以这里就能看出为什么侵入性是很弱的,自身 core 部分的实现就是纯 JS 能力。

javascript 复制代码
// 核心就这么一句
const slice = useSyncExternalStoreWithSelector(
  api.subscribe,
  api.getState,
  api.getServerState || api.getInitialState,
  selector,
  equalityFn,
)

那么,其实这里有个疑问,为什么这就生效了呢?这里和 Zustand 就没有太大关系了,这里解释一下 React 这里核心的实现,其实很好理解,React 拥有 Fiber 更新的能力,我们只需要在 Fiber Change 过程,添加一个 subscribe 的回调给到三方就可以实现能力。

javascript 复制代码
// React 中的核心连接实现
// packages/react-reconciler/src/ReactFiberHooks.js
function subscribeToStore<T>(
  fiber: Fiber,
  inst: StoreInstance<T>,
  subscribe: (() => void) => () => void,
): any {
  const handleStoreChange = () => {
    // The store changed. Check if the snapshot changed since the last time we
    // read from the store.
    if (checkIfSnapshotChanged(inst)) {
      // Force a re-render.
      forceStoreRerender(fiber);
    }
  };
  // Subscribe to the store and return a clean-up function.
  
  // 重点就是这里了~其实在内部是放了 forceStoreRerender 的回调
  return subscribe(handleStoreChange);
}

有深入兴趣的同学可以再研究了,其实从理解 Zustand 基本就是这些内容,毕竟我们不需要帮 React CV 一个 useSyncExternalStore。这里还有个可以研究的点就是 withSelector,可以看下基础的实现,是基于 memo state 的模式。

最后总结:zustand + React 是基于 useSyncExternalStore 能力实现的 Redux 😄

它的生态怎么样?是否有商用的案例?

  • 整体趋势
    • 可以看出基本已经是前三的位置,并且有上涨趋势,就像自己理解的,他可能是继承 Redux 模式的库
相关推荐
wangdaoyin20103 分钟前
UniApp 在手机端(Android)打开选择文件和文件写入
android·前端·uni-app
用户497357337988 分钟前
高端Web全栈工程师精品就业实战班课程 从零打造Web架构师
前端
我们没有完整的家15 分钟前
技术速递|Playwright MCP 调试 Web 应用时,GitHub Copilot 生成断言脚本的实用方法
前端·github·copilot
universe_0120 分钟前
前端学习之八股和算法
前端·学习
一川_29 分钟前
ElementUI分页器page-size切换策略:从保持当前页到智能计算的优化实践
前端
敲敲了个代码1 小时前
[特殊字符] Web 字体裁剪优化实践:把 42MB 字体包瘦到 1.6MB
前端·javascript·学习·html·web
扎瓦斯柯瑞迫1 小时前
Cursor 提示"Too Many Accounts"?一行命令重置机器码
前端·javascript·后端
前端付豪1 小时前
Vue3 响应式来!
前端·javascript·vue.js
芝士麻雀1 小时前
Zustand 深度解析:原理、源码与最佳实践
前端·react.js·前端框架
fruge2 小时前
前端性能优化实战指南:从首屏加载到用户体验的全面提升
前端·性能优化·ux