该篇文章适合的阅读人群:刚接手公司项目,技术栈为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. 整体开发流程?
- 编写你的业务组件
- 你意识到有些数据可能会跨组件使用
- 你为其定义了一个Model,声明了一个独一无二的namespace
- 你开始思考会涉及到哪些同步异步的状态处理
- 初始化state,编写effects、reducers(看上面例子的model部分!)
- 回到你的业务组件并将其连接到dva上(看上面例子的connect部分!)
- 在合适的地方dispatch一个action执行相应的reducer/effect(看上面例子的dispatch部分!)
请先阅读官网的这篇文章。
5. 更好的理解Dva?- 你需要熟悉一下redux的架构
- 你需要理解react-redux的作用
- 思考一下redux负责同步状态的管理,那么该如何处理异步状态?
- 看一下redux-thunk的源码,并了解redux是怎么用中间件思想让状态管理支持异步
- redux-saga解决了什么,它是如何处理异步状态的
- 整合构成了dva这一套状态管理的最佳实践,回过头来看看文档是不是很亲切了。