【FE】zustand顶层设计类比java去理解
Zustand 顶层设计 —— Java 程序员视角
核心类比
Zustand Store ≈ Spring Singleton Bean + 观察者模式 + AOP 切面
一、架构对应关系
| Zustand 概念 | Java/Spring 等价物 | 说明 |
|---|---|---|
create<AuthState>() |
@Component @Scope("singleton") |
创建全局单例 Bean |
useAuthStore |
@Autowired AuthStore |
依赖注入 |
set({ ... }) |
setState() + notifyListeners() |
更新状态并发布事件 |
subscribe(listener) |
@EventListener |
订阅状态变化 |
persist 中间件 |
@Aspect @Around |
AOP 切面增强 |
localStorage |
Redis / 文件存储 | 持久化层 |
二、状态容器等价实现
TypeScript (Zustand)
export const useAuthStore = create<AuthState>()(
persist(
(set) => ({
accessToken: null,
user: null,
login: (token, user) => set({ accessToken: token, user }),
}),
{ name: 'sea-world-auth' }
)
);
Java 等价代码
@Component
@Scope("singleton")
public class AuthStore {
private String accessToken = null;
private UserInfo user = null;
private List<Consumer<AuthState>> listeners = new CopyOnWriteArrayList<>();
// 核心方法:更新状态 + 通知订阅者 + 持久化
private void setState(Consumer<AuthStore> updater) {
updater.accept(this); // 更新字段
notifyListeners(); // 发布事件
persistToLocalStorage(); // 持久化(类似 @Transactional)
}
public void login(String token, UserInfo user) {
setState(store -> {
store.accessToken = token;
store.user = user;
});
}
private void notifyListeners() {
// 类似 Spring ApplicationEventPublisher
listeners.forEach(listener -> listener.accept(this));
}
}
三、关键概念详解
1. 选择器函数(Selector)—— 为什么用函数而不是对象?
问题:
// ❌ 订阅整个对象(性能差)
const store = useAuthStore(); // 任何字段变化都触发重新渲染
解决方案:
// ✅ 选择性订阅(性能优)
const login = useAuthStore(s => s.login); // 只有 login 变化才渲染
Java 类比:
// 类似方法引用
LoginMethod login = authStore.subscribe(AuthStore::getLogin);
// 内部实现
public <T> T subscribe(Function<AuthStore, T> selector) {
T currentValue = selector.apply(this);
this.addListener((newState, oldState) -> {
T newValue = selector.apply(newState);
T oldValue = selector.apply(oldState);
// ⚡ 只有选中的值变化才通知组件
if (!Objects.equals(newValue, oldValue)) {
component.rerender(newValue);
}
});
return currentValue;
}
性能对比:
useAuthStore()→ 订阅所有字段,重新渲染频繁 ❌useAuthStore(s => s.login)→ 只订阅 login,几乎不渲染 ✅
2. 中间件系统 —— AOP 切面编程
TypeScript:
create(
devtools( // 中间件 2:调试工具
persist( // 中间件 1:持久化
(set) => ({ ... })
)
)
)
Java 等价(AOP):
@Aspect
@Component
public class PersistenceAspect {
@Around("execution(* AuthStore.setState(..))")
public Object aroundSetState(ProceedingJoinPoint joinPoint) throws Throwable {
// 执行原始方法
Object result = joinPoint.proceed();
// 后置增强:持久化
AuthStore store = (AuthStore) joinPoint.getTarget();
localStorage.setItem("sea-world-auth", serialize(store));
return result;
}
}
洋葱模型:
set()
→ DevToolsAspect.before
→ PersistenceAspect.before
→ 原始 setState 执行
→ PersistenceAspect.after (持久化到 localStorage)
→ DevToolsAspect.after (发送到 DevTools)
3. 闭包机制 —— 内部方法如何访问 set?
TypeScript:
(set) => ({ // ← 外部函数
login: (token, user) => // ← 内部函数
set({ token, user }) // ← 访问外部的 set(闭包)
})
Java 等价(内部类 + Lambda):
public AuthState createState(SetState set) { // 外部方法
return new AuthState() {
@Override
public void login(String token, UserInfo user) { // 内部方法
// 访问外部方法参数 set(闭包)
set.apply(Map.of("accessToken", token, "user", user));
}
};
}
为什么需要闭包?
- 让
login()、logout()等方法能调用set()更新全局状态 - 避免每次都传递
set参数
四、完整调用链路
// 1. Controller 注入 Store
@Autowired
private AuthStore authStore;
// 2. 用户登录
public void handleLogin(LoginRequest req) {
AuthResponse res = authApi.login(req);
// 3. 调用 store 方法
authStore.login(res.getAccessToken(), res.getUser());
// =============== 内部执行 ===============
// 4. setState() 更新字段
// 5. AOP 切面拦截(持久化到 localStorage)
// 6. notifyListeners() 发布事件
// 7. 所有订阅组件收到通知并重新渲染
}
五、核心优势
| 特性 | Zustand | Redux |
|---|---|---|
| 样板代码 | 极少 ✅ | 多 ❌ |
| 学习曲线 | 平缓 ✅ | 陡峭 |
| Bundle 大小 | 1KB | 3KB |
| Provider 包裹 | 不需要 ✅ | 需要 |
| TypeScript | 原生支持 ✅ | 需额外配置 |
六、总结
对于 Java 程序员,理解 Zustand 的关键:
- 把 Store 想象成 Spring Singleton Bean —— 全局唯一,自动注入
- 把
set()想象成事务方法 —— 更新状态 + 发布事件 + 持久化一气呵成 - 把中间件想象成 AOP 切面 —— 拦截
set()调用,增强功能 - 把选择器想象成方法引用 —— 精准订阅,避免不必要的重新渲染
- 把闭包想象成内部类 —— 捕获外部变量,保持引用
核心思想:单一状态容器 + 发布订阅模式 + 中间件增强 = Zustand

浙公网安备 33010602011771号