第 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 版权所有