umi 中神奇的useModel
说明: umi会根据用户的配置生成代码,在临时文件 .umi/umi.ts 中;
umi.ts
// 记住,这俩都是 rootContainer 插件,在后面的特定时机执行;
plugin.register({
apply: Plugin_0, // initstate
path: '../plugin-initial-state/runtime',
})
plugin.register({
apply: Plugin_1, // useModel
path: '../plugin-model/runtime',
})
主流程
const getClientRender = () => plugin.applyPlugins({
key: 'render',
type: ApplyPluginsType.compose,
initialValue: () => {
const opts = plugin.applyPlugins({
key: 'modifyClientRenderOpts',
type: ApplyPluginsType.modify,
initialValue: {
routes: getRoutes(), // umi 中的路由表
history: createHistory(), // 作为 router 的props
rootElement: 'root',
},
});
return renderClient(opts);
},
args,
});
// 最终执行如下: 开始真正的去渲染应用
renderClient({
routes: getRoutes(), // umi 中的路由表
history: createHistory(), // 作为 router 的props
rootElement: 'root',
})
function renderClient(opts) {
var rootContainer = plugin.applyPlugins({
// reduce 的方式执行rootContainer插件
type: runtime.ApplyPluginsType.modify,
key: 'rootContainer',
initialValue: React.createElement(RouterComponent, {
history: opts.history,
routes: opts.routes,
}),
});
var rootElement = html模板中的root div;
reactDom.render(rootContainer, rootElement);
}
// 1. RouterComponent 其实就是
<Router history={createHistory()}>
{/* 根据路由表渲染 route 组件 */}
</Router>
// 2. initstate, 这里不做介绍
// 3. useModel
const models = { '@@initialState': initialState, 文件名: 函数 }
注意: 这个models对象是解析了model文件夹然后生成的一个对象最后写入这里的
class Dispatcher {
callbacks = {}
data = {}
update = namespace => {
this.callbacks[namespace].forEach(
callback => {
callback(this.data[namespace])
}
)
}
}
<UmiContext.Provider value={dispatcher}>
{
Object.entries(models).map(pair => (
<Exe
key={pair[0]}
namespace={pair[0]}
hook={pair[1]} // model 函数
onUpdate={val => {
const [ns] = pair
dispatcher.data[ns] = val
dispatcher.update(ns)
}}
/>
))
}
// initstate router 组件
{children}
</UmiContext.Provider>
// Exe空组件: 目标是为了借用组件域执行自定义的hook函数
props => {
const { hook, onUpdate, namespace } = props
let data = hook()
const updateRef = useRef(onUpdate);
updateRef.current = onUpdate;
const initialLoad = useRef(false);
// 如果在 hook 中 setState 了, 那么会重新执行这个组件,再次执行data = hook(),得到新数据。
// 触发 onUpdate(data) 更新dispatcher中的数据,最后触发callback,其实就是更新你用 useModel 的那个组件。
useEffect(() => {
// 第一次不走这里
if (initialLoad.current) {
updateRef.current(data)
} else {
initialLoad.current = true
}
})
return <></>
};
// useModel 的使用
// 其实也是一个自定义的hook,作用对象是你的组件;
function useModel(
namespace
) {
// 这个 dispatcher 其实保存了所有的useModel的数据
const dispatcher = useContext(UmiContext)
const [state, setState] = useState(
() => dispatcher.data[namespace]
)
useEffect(() => {
const handler = e => setState(e)
dispatcher.callbacks[namespace].add(handler)
dispatcher.update(namespace)
return () => dispatcher.callbacks[namespace].delete(handler)
}, [namespace])
return state
}