阅读 40

理解 dva

简介

官网上介绍:dva 是基于redux,redux-saga和react-router的轻量级前端框架。也就是 dva 主要是基于 redux 和 redux-saga 的数据流解决方案,内置了 react-router 和 fetch。

从何处开始接触

dva 是 antd design pro 基于 React 的中后台管理控制台的脚手架选择的数据流管理框架,做 antd-pro 搭建的项目的时候开始接触到这个框架,在脚手架形成的目录中:dva 分别在 '/src/models' 中管理全局数据,在 '/pages/确定的某一页面/models' 中管理某一页面的数据。

结合代码理解

下面代码是在 antd-pro 脚手架搭建起来的项目中的实践,原理部分的掌握还欠缺,先记下来常用的用法,和基础知识。

  1. 这可能是在 page 中的一段简单代码,组件要改变当前状态需要调用 dispatch 函数驱动 dva model state 的改变,改变后的 state 再通过 connect 注入到组件中:
// connect 也是连接的数据是对 store 中的数据的浅复制,当是引用类型的数据的时候,如果直接修改 props 中映射数据, store 里面的数据也会被修改
@connect(state => ({
  detailsTable: state['event/addNewCard'],// 这里传入的 state 参数是一个“完全体”,包含了所有 namespace 下的 state,以 namespace 为 key 值进行区分
  loading: state.loading,
  layout: state.layout,
}))
export default class PuzzleCardsPage extends Component {
  addNewCard(){
    dispatch({
        type:'event/addNewCard',
        payload:{},//传递数据的载体
        //还可以传进去一个回调函数,处理当数据没有正确请求的时候的处理逻辑
        callback: res => {
            message.error(res)
        }
    })
  }
  render() {
    return (
        <div>
          <Button onClick={this.addNewCard}> 添加卡片 </Button>
        </div>
    );
  }
}
复制代码
  1. model 是 dva 中最重要的一个概念,一个简单的 model 示例如下:
export default {
    namespace: '',//不同的页面或操作对应不同的命名空间
    state:{},
    effects:{//相当于 redux 的 middleware,为了保证 reducer 的纯粹性,有一些需要对数据在进行处理的操作放在 effects 里面,比如在 effects 里面可以调用 services 方法进行异步请求。
        *addByONe({payload, error}, { call, put, select }) { // 传入这个 Generator 函数的有两个参数,一个是匹配这个 effects 的 action 对象,因此可以取到 payload 和 error 字段。 另一个是 effect 原语集,call 主要处理异步逻辑(比如发起请求)它第一个参数是一个函数,要求函数返回 Promise,之后的参数是该函数调用时的入参。
          const num = yield select(state => state.num)    // effects 里面主要通过yield select 获取 state
          let param1;
          param1 = num + param;
          yield put({//effects 里面通过 put 方法触发 action,和 pages 中的dispatch 方法一模一样,可以继续请求其他 namespace 中的 effects 或者 reducer ,不同的是,请求自身的 effects 或者 reducer 时不需要指定 namespace 名称
             type: 'save',   
             num:param1
      });
    }
    },
    //用来保存更新 state 值 上面的 put 方法调用这里的方法,当不需要发起请求,只修改state中数据的方法放在这里,且这里的方法也能够被外部调用
  reducers: {
    save(state, action) { //state 是当前命名空间的 state,这里的 action 包含了上面传递的参数和 type
      return { ...state, ...action.num }; 
    },
  },
}
复制代码

model 中几个基本属性,其中 namespace 和 state 是一个 model 中最基本的两个成员。

  • namespace:model 的命名空间,只能用字符串,是一个 model 的唯一标识。一个大型应用可能包含多个 model,通过 namespace 区分。
  • state:当前 model 状态的初始值,表示当前状态。是该 model 管理的数据
  • reducers:用于处理同步操作,更新修改 state,由 action 触发。reducer 是一个纯函数,它接受当前的 state 及一个 action 对象。action 对象里面可以包含数据体(payload)作为入参,需要返回一个新的 state。
  • effects:用于处理异步操作(例如:与服务端交互)和复杂的数据处理逻辑,也是由 action 触发。但是,它不可以修改 state,要通过触发 action 调用 reducer 实现对 state 的间接操作。
  • action:是 reducers 及 effects 的触发器,一般是一个对象,形如{ type: 'add', payload: todo },通过 type 属性可以匹配到具体某个 reducer 或者 effect,payload 属性则是数据体,用于传送给 reducer 或 effect。
  1. 如果想知道数据请求函数是否执行完毕,需要加载 dva-loading 插件,但是使用脚手架搭建项目,直接在页面中使用就可以了,通过 connect 方法将加载的状态传递到组件中,下面是几种具体的方法:

(1)loading.effects['chart/fetch'] 监听这个方法是否执行完毕,若执行完毕。loading 的值就变为 false。

@connect(({ chart, loading }) => ({
  chart,
  loading: loading.effects['chart/fetch'],
}))
复制代码

(2)loading.models.monitor 监听这个模块是否执行完毕,注意这里的 [monitor] 是命名空间。监听的是当前页面用到的命名空间里面的方法。若执行完毕,loading 的状态变为 false。

@connect(({ monitor, loading }) => ({
  monitor,
  loading: loading.models.monitor,
}))
复制代码

(3)还有一种用法是 loading.global,通过全局的状态控制 loading 的状态。loading.global监听这个页面的加载状态。当页面处于异步加载状态时该值为 true,当页面加载完成时,自动监听该值为 false。

这里是绑定到 dva 的自动控制 loading 数据的插件。不再需要重复写 showLoading 和 hideLoading。

当不使用 antd-pro 脚手架搭建项目使用 dva 时

文档中讲的很清楚,而且在掌握上面的基础知识后,也不难上手使用这个框架。

// 创建应用
const app = dva();

// 注册 Model
app.model({
  namespace: 'count',
  state: 0,
  reducers: {
    add(state) { return state + 1 },
  },
  effects: {
    *addAfter1Second(action, { call, put }) {
      yield call(delay, 1000);
      yield put({ type: 'add' });
    },
  },
});

// 注册视图
app.router(() => <ConnectedApp />);

// 启动应用,将组件挂载到 id 为 root 的元素上
app.start('#root');
复制代码

参考

dva-loading 实践用法
github
dva官网指南

关注下面的标签,发现更多相似文章
评论