1.为什么?
使用umijs 也有很久时间了,他的一些插件(qiankun,antd...)等等都用了不少,但是一直未接触到dva的全局状态管理。为什么呢?
1. 项目中比较少用到全局状态,基本是后台的id相互传递信息
2. 自己根据上下文(createContext,useReducer)写了一个较小的状态管理,效果尚可,我甚至基于这个写了一套i18n。但每个组件都需要引入上下文函数,有些小麻烦
1. 下一个项目开始准备使用较为成熟的全局状态管理,技术难度上dva更适合
2. 适配开发使用的主要框架umi
3. 对vuex尚有了解并使用过,这次学习属于知识一个储备过程
2.dva 简介
dvajs
umi 中使用 dva 则可以直接下载插件进行配置即可。我等菜狗福音。
3.dva 模型理解(for me)
1. global
dva 的 global 模型,包含 dispatch 触发函数,当前路由的一些属性等,我们把这个看作是一个全局对象(root)。
{
children: {$$typeof: Symbol(react.element), key: null, ref: null, props: {…}, type: ƒ, …},
dispatch: ƒ (action),
history: {length: 21, action: "POP", location: {…}, createHref: ƒ, push: ƒ, …},
location: {pathname: "/", search: "", hash: "", query: {…}, state: undefined},
match: {path: "/", url: "/", isExact: true, params: {…}},
route: {path: "/", component: {…}, exact: true},
routes: (2) [{…}, {…}],
staticContext: undefined
}
2. model
dva 拆解的一个局部状态操作模型。里头包含主要五个属性:namespace,state,reducer,effects,subscriptions。models 彼此相对独立,但是也可以彼此操作。
3. namespace
顾名思义,命名空间,他决定了这个 model 所处的模块。通过 dva 编译以后会挂载到 global 中。形如
{
...
route: {path: "/", component: {…}, exact: true},
routes: (2) [{…}, {…}],
staticContext: undefined,
user:{name:"sanfeng"}
}
就是将命名空间为user,状态值为 {name:"sanfeng"} 的 model 挂载到 global 中了。方便后续的触发操作
4. state
状态。基本是一个对象值,但是好像可以是任意类型的值。值会被挂载到 key 为 namespace 的 global 中。
5.action
action 是一个普通 javascript 对象,它是改变 State 的唯一途径。action 必须带有 type 属性指明具体的行为,其它字段可以自定义。如
dispatch({ type: "user/change", name: "ruansanfeng" });
type指明了触发的函数为 namespace 为 user 的 model,并执行 reducer 或者 effects 中的 action。注意:相同 model 中,reducer 或者 effects 的 key 不能重名。其余的key-value则表明传递的参数值。我们习惯上用 payload 这个 key 来传递所有参数。
6.reducers
同步更改状态。熟悉 vuex 应该都知道,状态更改是分同步异步的。reducers 是一个对象,values 为函数。入参有两个:state 表示当前状态,action 表示入参值。
reducers: {
// 写法一
save: (state, action) => ({
...state,
...action.payload,
}),
// 写法二
delete(state,action){
return{
...state,
list:state.list.filter(it=>it.id!=action.payload.deleteId)
}
}
},
以上两种写法均可。
7.effects
异步更改状态。一般后台数据请求就写在这个方法中。effects 也是一个对象,所以整体写法上可以和 reducers 类似。但是由于其中存在异步,所以需要使用 Generator 函数的语法,这里比较纳闷,async 和 await 应该才是更直观的语法,这里却硬性要求,改变了就不能更改状态了。 函数的入参也有所改变。第一个入参为 action,即我们传递的入参值。第二个入参为一类操作集,其中最重要的为 call 和 put。
effects: {
*query({ payload }, { call, put }) {
const data = yield Promis();
yield put({type:'save',payload:data})
},
},
其中 call 表示执行异步函数,用的较少。put 则是发出一个 action,类似于 dispatch。不同的是,触发本 model 的 action 不需要加 namespace 和斜杆,如上例的 save。
8.subscriptions
Subscription 语义是订阅,用于订阅一个数据源,然后根据条件 dispatch 需要的 action。数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等。这里的订阅其实挺复杂的,更多的情况需要第三方库来处理。具体请看这里
4.绑定到 UI
import { IndexModelState, connect } from "umi";
export default connect(({ user }: { user: IndexModelState }) => ({
user,
}))(({ user, dispatch }) => {
///react ui
});
model 中到处的 state 类型,可以通过 umi 引进来,强的一批。通过从 umi 中引入 connect 方法来绑定组件,connect 参数部分别看这个花里胡哨的,还有点柯里化,其实就两个,第一个是一个匿名回调函数,返回当前模块需要的 model 的 namespace 即可。第二个就是 react 组件,入参为 global,通过结构就可以拿到想要的状态。同事可以获取 dispatch 来触发 action。
作者信息:张栓,人和未来前端工程师。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!