第 56 期 - 剖析 Zustand 源码中的 TS 类型运用与实现原理
摘要
文章介绍 Zustand 为轻量的 react 状态管理库,详细解读 store 创建、状态类型相关的源码内容,包括 StateCreator、Mutate 等类型,还涉及中间件实现、createImpl 函数,最后总结 Zustand 的特性并分享开源项目
一、Zustand 状态管理库概述
Zustand 是一个非常轻量的 react 状态管理库,它借力于 react hooks,其源码精简且使用方法简单。
二、store 创建逻辑
1. create 方法
- 在
create方法中,createState是一个可选参数,类型为StateCreator<T, [], []> | undefined。 - 如果
createState存在(传入了状态创建函数),则调用createImpl(createState);若不存在(未传入状态创建函数),则直接返回createImpl函数本身。例如:
export const create = (<T>(createState: StateCreator<T, [], []> | undefined) =>
createState? createImpl(createState) : createImpl) as Create;
这一设计可以在需要更高灵活性时,通过获取底层的createImpl函数来实现自定义的状态初始化和扩展,支持如延迟初始化、动态创建、多实例化以及中间件集成等复杂的状态管理需求。
三、重要的类型定义
1. StateCreator 类型
- 它是一个函数类型,负责创建状态的初始值。其定义如下:
export type StateCreator<
T,
Mis extends [StoreMutatorIdentifier, unknown][] = [],
Mos extends [StoreMutatorIdentifier, unknown][] = [],
U = T> = ((
setState: Get<Mutate<StoreApi<T>, Mis>, "setState", never>,
getState: Get<Mutate<StoreApi<T>, Mis>, "getState", never>,
store: Mutate<StoreApi<T>, Mis>) => U) & { $$storeMutators?: Mos };
- 这里的
T是初始状态的类型,Mis是输入的mutators列表(默认空数组),用于在创建状态时进行状态变换或增强,Mos是输出的mutators列表(默认空数组),U是返回的状态类型(默认与T相同)。 - 开发者可以在创建状态时传入多个
mutators,这些mutators会按顺序应用到状态管理实例上以增强功能。
2. SetStateInternal 类型
- 定义了一种内部更新状态的方法。
type SetStateInternal<T> = {
_(
partial: T | Partial<T> | { _(state: T): T | Partial<T> }["_"],
replace?: false
): void;
_(state: T | { _(state: T): T }["_"], replace: true): void;
}["_"];
- 它的
_方法支持两种更新方式,一种是部分更新(replace为false或不传入),参数partial可以是完整状态对象T、部分状态Partial<T>或者返回新状态或部分状态的函数;另一种是完全替换状态(replace为true),参数state是新的完整状态。
3. Get 类型
Get是一个条件类型,用于从类型T中提取键K的类型。
type Get<T, K, F> = K extends keyof T? T[K] : F;
例如在获取用户名字类型时,如果K(这里是"name")存在于T(User类型)中,则返回T[K](即string类型),否则返回默认类型F。
4. Mutate 类型
- 这是一个递归类型,用于对状态类型
S应用一系列的“变换器”(mutators)。
export type Mutate<S, Ms> = number extends Ms["length" & keyof Ms]
? S
: Ms extends []
? S
: Ms extends [[infer Mi, infer Ma],...infer Mrs]
? Mutate<StoreMutators<S, Ma>[Mi & StoreMutatorIdentifier], Mrs>
: never;
- 如果
Ms是动态长度的数组则返回原始状态类型S;如果Ms是空数组,表示没有变换,也返回原始状态S;否则提取第一个mutator元组并对状态类型进行变换,然后递归调用Mutate继续处理剩余的mutators。
四、中间件相关实现
- 中间件可以拦截状态变化,并在处理前后执行一些逻辑。为了让代码更符合中间件特点,做了如下改进:
- 增加中间件控制权,允许中间件决定是否继续执行下一个中间件。
- 为每个中间件提供
next函数,使其可以显式调用下一个中间件。例如在StoreMutators接口中:
interface StoreMutators<S, A> {
logging: (state: S, action: A, next: (newState: S) => S) => S;
timestamp: (state: S, action: A, next: (newState: S) => S) => S;
}
- 在实际的
mutators实现中,logging中间件会打印状态并调用next函数传递状态,timestamp中间件会添加时间戳后调用next函数。
五、createImpl 函数
createImpl函数最后返回了useBoundStore方法,这个方法基于useStore和api。useStore基于react hook中的useSyncExternalStore,这是一种状态更新方案。- 在
createImpl中,api对象是通过调用createStore方法并传入createState参数得到的(假设createState是一个函数),而createStore实则是将createState透传给createStoreImpl方法。 - 在
createStoreImpl函数中,通过zustand管理的状态state是createState经过一系列透传后在createStoreImpl里执行返回的值,而函数最后返回的是一个包含setState、getState、subscribe等方法的api对象,管理的状态state以闭包形式存在。
六、关于内存泄漏问题
- 在
zustand中,create函数只会被执行一次,并且其内部机制确保每次调用useXXX(由create函数返回的useStore钩子函数)时都是使用同一个store实例,不会重复创建多个store,所以不会出现内存泄漏问题。 - 当调用
create函数时,会创建一个状态管理实例,包含状态管理方法并在内存中保持引用,zustand通过闭包将实例包装在useStore钩子函数中返回。每次调用useStore访问的都是同一个实例,且通过subscribe方法,在组件卸载时会自动移除监听器,防止订阅关系残留导致内存泄漏。
七、Zustand 的总结
- Zustand 通过简单的状态存储对象和函数式 API 提供灵活的状态管理方式。
- 它的核心特点是轻量、灵活、易于集成,通过
useSyncExternalStore实现高效的状态订阅和组件重渲染机制。 - 支持中间件扩展功能,如日志记录、持久化和调试工具集成,适应现代
React的并发模式和服务器端渲染(SSR)需求。 - 最后作者还分享了自己的两个开源项目,分别是前端脚手架
create - neat和在线代码协同编辑器,并欢迎大家参与交流学习。
扩展阅读
Made by 捣鼓键盘的小麦 / © 2025 Front Talk 版权所有
