DVA框架学习-01

       新入职的公司使用dva框架,之前没有接触过,所以一边学习,一边工作。记一下DVA框架的几个基本概念。

首先放一下DVA框架数据流向:

image

       数据的改变发生通常是通过用户交互行为或者浏览器行为(如路由跳转等)触发的,当此类行为会改变数据的时候可以通过dispatch发起一个action,如果是同步的行为,会直接通过reducer改变state,如果是异步的行为(副作用)会先触发effects然后流向reducer,最终改变state(一般是从服务器获取数据,拿到数据后触发effects),然后通过connect(一个函数,绑定state到view)渲染组件。所以在DVA中,数据流非常清晰简明,并且思路基本跟开源社区保持一致。

DVA的几个概念

Models组成:namespace,state,reducer,effect, subscription

{
  namespace: 'count',
  state: 0,
  reducers: {
    add(state) { return state + 1 },
  },
  effects: {
    *addAfter1Second(action, { call, put }) {
      yield call(delay, 1000);
      yield put({ type: 'add' });
    },
  }
},
subscription{
}

State

       表示该model当前的状态。数据保存在这里,直接决定了视图层的输出。
       State表示Model的状态数据,通常表现为一个js对象(当然可以是任何值);操作的时候每次都要当做不可变数据(immutable data)来对待,保证每次都是全新对象,没有引用关系,这样才能保证State的独立性,便于测试和追踪变化。

state:{
  namespace:'businessApprove',
  nodeList:[],
  checkList:[]
}

       在DVA中,你可以通过DVA的实例属性_store来看到顶部的state数据,但是通常很少用到:

const app = dva();
console.log(app._store);//顶部的state数据

Action

       一个对象,描述事件,可以是同步,也可以是异步。
Action是一个普通js对象,他是改变State的唯一途径。无论是从UI事件,网络回调,还是WebSocket等数据源说获得的数据,最终都会通过dispatch函数调用一个action,从而改变对应的数据。action必须带有type属性指明具体的行为,其他字段可以自定义。如果要发起一个action需要使用dispatch函数,需要注意的是dispatch是在组件connect Models以后通过props传入的,通过connect将model中的元素作为props的方式传递给component。

dispatch({
   type:'changeData',
   payload:{
      list:[],
    }
})
export default connect()(BusinessApprove)

connect

    通过connect将module中的元素作为props的方式传递给component。

export default connect(({ businessApprove, loading }) => ({ businessApprove, loading }))(BusinessApprove)

dispatch函数

    dispatch函数是一个用于触发action的函数,action是改变state的唯一途径,但是他只是描述了一个行为,而dispatch可以看做是触发这个行为的方式,而reducer则是描述如何改变数据的。
    在DVA中,connect Model的组件通过props可以访问到dispatch,可以调用Model中的reducer或者effects,常见的形式如:

   dispatch({
        type:`${namespace}/changeData`,
       //如果在model外调用,需要添加namespace,(`${namespace}`)
        payload: {//需要传递的信息
          list: [],
        }
    })

reducer

type Reducer<S,A> = (state:S,action:A) =>S
    reducer更新数据,Action处理器,处理同步动作,用来算出最新的state。
    reducer函数接受两个参数:之前已经累积运算的结果和当前要被累积的值,返回的是一个新的累积结果。该函数把一个集合归并成一个单值。reducer的概念来自于函数式编程。在DVA中,reducer聚合累积的结果是当前model的state对象。通过action中传入的值,与当前reducer中的值进行运算,获得新的值(也就是新的state)。需要注意的是,reducer必须是纯函数,所以同样的输入必然得到同样的输出,它们不应该产生任何副作用。并且,每一次的计算都应该使用immutable data,这种特性简单理解就是每次操作都是返回一个全新的数据(独立、纯净),所以重热载和时间旅行这些功能才能使用。

  reducers: {
    changeData (state, { payload }) {
      return { ...state, ...payload }
    },
    ...
  }

effect

    请求数据,action处理器,处理异步动作。底层引入了redux-saga做异步流程控制,采用了generator的相关概念。effect是一个generator函数,内部使用yeild关键字,标识每一步的操作(不管是异步或者同步)。
    effect被称为副作用。在我们的应用中,最常见的就是异步操作。它来自于函数编程的概念,之所以叫副作用是因为它使我们的函数变得不纯,同样的输入不一定获得同样的输出。DVA为了控制副作用的操作,底层引入了redux-saga做异步流程控制,由于采用了generator的相关概念,所以将异步转成同步写法,从而将effect转换为纯函数。

effects: {
    // 查询付款列表
    * query ({ payload = {} }, { call, put, select }) {
      const data = yield call(query, payload)//发起ajax请求 service内的query异步函数
      // const { tableKey } = yield select(({ paymentApprove }) => paymentApprove)//获取model中的state
      if (data.success) {
        yield put({//提交reducer更改state
          type: 'changeData',
          payload: {
            list: data.data.list,
          },
        })
      } else {
        message.error(data.message, 1)
      }
    },

call和put

    DVA提供多个effect函数内部的处理函数,比较常用的是call和put。
    call:执行异步函数
    put:发出一个action,调用给定的函数,类似于dispatch
    select:从全局中取数据

const num = yield select(
    state => state.count.num
 );

subscription

    用于订阅一个数据源,然后根据条件dispatch需要的action。数据源可以是当前的时间,服务器的websocket连接,keyboard的输入,geolocation变化,history路由变化等等。subscription中无法监听state中的数据变化。
    model分两类,一是全局model,二是页面model。全局model存在于/src/model/目录,所有页面都可以引用,页面model不能被其他页面所引用。
    规则如下:

  • src/models/*/.js 为 global model
  • src/pages//models//*.js 为 page model
  • global model 全量载入,page model 在 production 时按需载入,在 development 时全量载入
  • page model 为 page js 所在路径下 models/*/.js 的文件
  • page model 会向上查找,比如 page js 为 pages/a/b.js,他的 page model 为 pages/a/b/models//.js + pages/a/models//.js,依次类推
  • 约定 model.js 为单文件 model,解决只有一个 model 时不需要建 models 目录的问题,有 model.js 则不去找 models/*/.js
    dva01-02.png

    如上目录:
    ● global model 为 src/models/g.js
    ● /a 的 page model 为 src/pages/a/models/{a,b,ss/s}.js
    ● /c 的 page model 为 src/pages/c/model.js
    ● /c/d 的 page model 为 src/pages/c/model.js, src/pages/c/d/models/d.js
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容