简介

中后台场景下,绝大多数页面的数据流转都是在当前页完成,在页面挂载的时候请求后端接口获取并消费,这种场景下并不需要复杂的数据流方案。但是也存在需要全局共享的数据,如用户的角色权限信息或者其他一些页面间共享的数据。那么怎么才能缓存并支持在多个页面直接去共享这部分数据呢。

为了实现在多个页面中的数据共享,以及一些业务可能需要的简易的数据流管理的场景,我们基于 hooks & umi 插件实现了一种轻量级的全局数据共享的方案。实现上我们通过 @umijs/plugin-model 插件来实现一个轻量级的方案来支持这样的需求,该插件已内置在 bigfish@3 中。

在学习 Ant Design Pro 之,我首先学习的 redux,之后有学习了mobx,感觉mobx是要比redux简单一点,之后在学习 Ant Design Pro V4 后接触到了 dva,又感觉简化了好多,过了几个月,V5发布了,然后看到了V5的数据流,简直成傻瓜式操作~,不得不说~,我钦佩蚂蚁团队~

当然 dva 在 V5 中 也可以使用,至于怎么使用,可以看看 这位大神的~~~ 聊聊 effects 与 reducers

废话说的有点多~,我们来看看具体怎么使用~

全局状态 initialState

首先,我们先来说说 V5 自带的initialState吧,用官方的话说是:

initialState 在 v5 中替代了原来的自带 model,global,login,setting 都并入了 initialState 中。我们需要删除 src/models/global.ts,src/models/login.ts,src/models/setting.ts ,并且将请求用户信息和登陆拦截放到 src/app.tsx 中。

理解下官方的话:

  • 首先,他是一个由官方内置的model,可以当做最外层的model
  • 其次,我们可以将全局的状态放进去,以供单独的文件去调用,如:用户信息,权限等级等~
  • 在这里,我把一开始调用的位置放在了 **/scr/utils/initData** 文件下,方便调用

我们先来看看如何使用吧

  1. import { useModel } from 'umi';
  2. export default () => {
  3. const { initialState, loading, error, refresh, setInitialState } = useModel('@@initialState');
  4. return <>{initialState}</>
  5. };

使用起来非常的方便,介绍下所有参数的用途

**initialState** : 返回全局状态,也就是 getInitialState 的返回值
**setInitialState**: (state:any) => 手动设置 initialState 的值,手动设置完毕会将 loading 置为 false.
loading: getInitialState 是否处于 loading 状态,在首次获取到初始状态前,页面其他部分的渲染都会被阻止。loading 可用于判断 refresh 是否在进行中。
error: 当运行时配置中,getInitialState throw Error 时,会将错误储存在 error 中。
refresh: () => void 重新执行 getInitialState 方法,并获取新数据。

单独设置

我们先说说存放的文件位置:src/model

为了更好的说明,我们来举个小例子~

文件位置 src/models/test/modelTest.ts

  1. import { useState, useCallback } from 'react';
  2. interface Props {
  3. count?: number
  4. }
  5. const initInfoValue: Props = {
  6. count: 1,
  7. }
  8. export default function modelTest() {
  9. const [init, setInitValue] = useState(initInfoValue);
  10. const [loading, setLoading] = useState(false);
  11. const waitTime = (time: number = 2000) => {
  12. return new Promise((resolve) => {
  13. setTimeout(() => {
  14. resolve(true);
  15. }, time);
  16. });
  17. };
  18. const setInit = useCallback(async(res:any) => {
  19. setLoading(true)
  20. await waitTime()
  21. setLoading(false)
  22. setInitValue({count: res})
  23. }, [init])
  24. const setAdd= useCallback((res:any) => {
  25. setInitValue({ count: res +1})
  26. }, [init])
  27. return {
  28. loading,
  29. init,
  30. setAdd,
  31. setInit
  32. };
  33. }

在这边做了 init 值, loading 状态, setaAdd 和 setInit 两个方法,下面我们将看看如何调用

  1. import React from 'react';
  2. import { useModel } from 'umi';
  3. import { Button } from 'antd';
  4. export const MockModel: React.FC<any> = () => {
  5. const { init, setInit, setAdd, loading } = useModel('test.modelTest');
  6. return <div>
  7. <div style={{ marginBottom: 14 }}> count 对应的值{init.count}</div>
  8. <Button loading={loading} style={{ marginBottom: 18 }} type='primary' onClick={() => setInit(5)} >设置count5</Button>
  9. <br />
  10. <Button type='primary' onClick={() => setAdd(init.count)} >累加1</Button>
  11. </div>
  12. }

只要把对应的方法,值全部返回,然后在调用即可,真是简单的过分了~~~

性能优化

useModel 第二个可选参数可用作性能优化,只消费 model 中的部分参数,而不使用其他参数, 并且返回的值则是 useModel 最终的返回值

我们以上述为例

  1. import React from 'react';
  2. import { useModel } from 'umi';
  3. import { Button } from 'antd';
  4. export const MockModelRet: React.FC<any> = () => {
  5. const { init, setAdds } = useModel('test.modelTest', (ret) => {
  6. return {
  7. init: ret.init,
  8. setAdds: ret.setAdd
  9. }
  10. });
  11. return <div>
  12. <div style={{ marginBottom: 14 }}> count 对应的值{init.count}</div>
  13. <Button type='primary' onClick={() => setAdds(init.count)} >累加1</Button>
  14. </div>
  15. }

案例地址

image.png