Umi中 useModel 实现
date
Oct 12, 2022
slug
/usemodel
status
Published
tags
umi
useModel
summary
Umi中 useModel 实现
type
Post
useModel 概述
useModel 是 umi 提供的一个数据流方案,它允许在组件中方便地使用和管理全局状态。
核心实现原理
- Provider 机制: 通过 React Context 在应用顶层提供状态管理能力
- 状态初始化: 在应用启动时,自动加载 models 目录下的模型文件
- Hook 实现: useModel 本质是一个自定义 Hook,内部使用 useContext 获取状态
详细实现步骤
- 创建全局 Model 容器,用于存储所有的 model 实例
- 通过插件系统扫描 src/models 目录,自动注册模型文件
- 实现 Provider 组件,包装整个应用并提供状态共享环境
- 实现 useModel hook,处理状态订阅和更新逻辑
核心代码示例
// useModel 的基本实现 function useModel(namespace: string, updater?: (model: any) => any) { const dispatcher = useContext(Context); const updaterRef = useRef(updater); updaterRef.current = updater; const [state, setState] = useState(() => updaterRef.current ? updaterRef.current(dispatcher.data[namespace]) : dispatcher.data[namespace] ); useEffect(() => { const handler = (data: any) => { if (updaterRef.current) { setState(updaterRef.current(data)); } else { setState(data); } }; return dispatcher.subscribe({ namespace, handler, }); }, [namespace]); return state; }
使用优势
- 简化的 API:相比 Redux 等状态管理方案,使用更加简单直观
- 按需加载:支持动态加载 model,优化性能
- TypeScript 支持:完善的类型推导,提供更好的开发体验
- 自动化管理:无需手动注册 model,降低使用成本
注意事项
- 避免过度使用全局状态,合理划分 model 范围
- 注意性能优化,合理使用 updater 函数进行选择性更新
- 保持 model 文件结构清晰,便于维护和扩展
性能优化原理解析
1. 订阅机制
useModel 内部实现了一个精确的订阅发布机制:
// 简化的订阅原理 const [state, setState] = useState(() => updaterRef.current ? updaterRef.current(dispatcher.data[namespace]) : dispatcher.data[namespace] ); useEffect(() => { const handler = (data) => { if (updaterRef.current) { const nextState = updaterRef.current(data); // 关键:只有当值真正变化时才触发更新 setState(nextState); } }; return dispatcher.subscribe({ namespace, handler }); }, [namespace]);
2. 更新判断机制
- 浅比较: React 内部使用 Object.is 进行状态比较,当新旧状态相同时不触发重渲染
- 选择性更新: updater 函数返回的对象如果没有变化,setState 不会导致组件重新渲染
3. 数据流优化
通过 updater 函数,我们可以实现:
- 数据过滤: 只订阅真正需要的数据字段,减少不必要的状态更新
- 计算缓存: 可以在 updater 中进行数据转换,类似 reselect 的效果
// 优化示例 const userDisplayInfo = useModel('user', (model) => ({ displayName: `${model.firstName} ${model.lastName}`, // 只订阅需要的字段,其他字段变化不会触发更新 avatar: model.avatar }));
4. 内存优化
- 引用维持: 使用 useRef 保存 updater 函数,避免不必要的重复订阅
- 清理机制: useEffect 返回的清理函数会在组件卸载时自动取消订阅