阅读 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 模式的库
相关推荐
梦境之冢35 分钟前
axios 常见的content-type、responseType有哪些?
前端·javascript·http
racerun38 分钟前
vue VueResource & axios
前端·javascript·vue.js
m0_548514771 小时前
前端Pako.js 压缩解压库 与 Java 的 zlib 压缩与解压 的互通实现
java·前端·javascript
AndrewPerfect1 小时前
xss csrf怎么预防?
前端·xss·csrf
Calm5501 小时前
Vue3:uv-upload图片上传
前端·vue.js
浮游本尊1 小时前
Nginx配置:如何在一个域名下运行两个网站
前端·javascript
m0_748239831 小时前
前端bug调试
前端·bug
m0_748232921 小时前
[项目][boost搜索引擎#4] cpp-httplib使用 log.hpp 前端 测试及总结
前端·搜索引擎
新中地GIS开发老师1 小时前
《Vue进阶教程》(12)ref的实现详细教程
前端·javascript·vue.js·arcgis·前端框架·地理信息科学·地信
m0_748249541 小时前
前端:base64的作用
前端