该篇文章适合的阅读人群:刚接手公司项目,技术栈为react,状态管理直接用的dva,但是不太了解redux、redux-saga、react-redux的人员,想要快速上手业务开发,看这篇就对了!
1. Dva是什么?

dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架。

2. 快速上手开发?(保存用户信息为例)
  • 定义Model文件用于全局的用户信息数据管理,如下代码:
// 引入接口方法用于在effects中编写所需的业务逻辑
import { queryCurrent } from '@/services/user'

// 实际上只是按规则定义一个配置对象
const UserModel = {
  // 当前 Model 的名称。整个应用的 State,由多个小的 Model 的 State 以 namespace 为 key 合成
  namespace: 'user',
  // 该 Model 当前的状态。数据保存在这里,直接决定了视图层的输出
  state: {
    currentUser: {}
  },
  // Action 处理器,处理异步动作,基于 Redux-saga 实现。Effect 指的是副作用。根据函数式编程,计算以外的操作都属于 Effect,典型的就是 I/O 操作、数据库读写。
  effects: {
    // Effect 是一个 Generator 函数,内部使用 yield 关键字,标识每一步的操作(不管是异步或同步)。
    *fetchCurrentUser({ payload, callback }, { call, put }) {
      // 执行异步函数
      const response = yield call(queryCurrent)
      if(response.success) {
        const { user } = response
        if(callback) {
          callback(user)
        }
        // 发出一个 Action,类似于 dispatch
        yield put({
          type: 'saveCurrentUser',
          payload: user
        })
      }
    }
  },
  // Reducer 是 Action 处理器,用来处理同步操作,可以看做是 state 的计算器。它的作用是根据 Action,从上一个 State 算出当前 State。
  reducers: {
    saveCurrentUser(state, { payload = {} }) {
      return {
        ...state,
        currentUser: payload
      }
    }
  }

}

export default UserModel;
  • 使用connect连接上Dva并使用,如下代码:
import { connect } from 'dva'

const SetUserInfoComponent = props => {
  const { dispatch } = props

  // 1. 调用dva附加在组件props上的dispatch方法,发起一个action
  // 2. action是一个普通对象(plain object),一般包含type字段和payload字段(当然也可以视情况不传payload)
  dispatch({

    // 3. type的值实际上类似一个寻址过程,编写规则即namespace/reducerName|effectName

    // 4. dva根据你的namespace去找到对应的model,根据你的reducerName|effectName找到对应的reducer或者effect

    // 5. 回到我们这个例子即:找到namespace为user的model,并触发一个名为fetchCurrentUser的effect,payload为空。

    // 注意:实际上第5点描述的就是这个对象的键值对,这就是action,一个描述动作的普通对象。
    type: 'user/fetchCurrentUser'
  })
}

export default connect(({ user }) => ({ user }))(SetUserInfoComponent)

以下部分是详细说明:connect是一个高阶函数,它接收一个mapStateToProps函数(当然你也可以随便叫它什么名字,无非这个名字比较能阐明它的作用), 返回一个接收组件的函数。该组件增强函数是一个普通函数,接收组件,返回增强后的组件。使得增强后的组件可以在内部通过props拿到dva中的state、dva提供的dispatch函数。

// connect第一个形参函数,描述了需要从全局state映射到组件props上的指定部分状态,该函数返回了一个对象内含状态键值对
const mapStateToProps = ({ user }) => ({ user })

connect(mapStateToProps)(Component) => <EnhancedComponent {...props, user: GlobalState[user], dispatch: fn(){/* dispatch function code*/}}/>

//我们可以简单想一下connect的实现形式可能类似如下代码帮助理解
const connect = mapStateToPropsFunc => MetaComponent => {

  const state = mapStateToPropsFunc(dva.GlobalState)

  return <MetaComponent dispatch={dva.dispatch} {...state}/>
}
实际上翻阅Dva源码可以知道connect方法就是由react-redux提供的(源码看这里),Dva做的工作就是引入导出connect便于我们统一在Dva中调用,即所谓的技术收敛。
3. 整体开发流程?
  1. 编写你的业务组件
  2. 你意识到有些数据可能会跨组件使用
  3. 你为其定义了一个Model,声明了一个独一无二的namespace
  4. 你开始思考会涉及到哪些同步异步的状态处理
  5. 初始化state,编写effects、reducers(看上面例子的model部分!)
  6. 回到你的业务组件并将其连接到dva上(看上面例子的connect部分!)
  7. 在合适的地方dispatch一个action执行相应的reducer/effect(看上面例子的dispatch部分!)
4. 想要了解Dva源码?

请先阅读官网的这篇文章。

5. 更好的理解Dva?
  • 你需要熟悉一下redux的架构
  • 你需要理解react-redux的作用
  • 思考一下redux负责同步状态的管理,那么该如何处理异步状态?
  • 看一下redux-thunk的源码,并了解redux是怎么用中间件思想让状态管理支持异步
  • redux-saga解决了什么,它是如何处理异步状态的
  • 整合构成了dva这一套状态管理的最佳实践,回过头来看看文档是不是很亲切了。