DvaJS笔记

简介

  • dva 是一个基于 reduxredux-saga 的数据流方案

  • dva = React-Router(路由) + Redux(架构) + Redux-saga(异步操作)

  • dva 通过 model 的概念把一个领域的模型管理起来,包含同步更新 statereducers,处理异步逻辑的 effects,订阅数据源的 subscriptions

数据流向

  • 数据的改变发生通常是通过用户交互行为或者浏览器行为(如路由跳转等)触发的

  • 当此类行为会改变数据的时候可以通过 dispatch 发起一个 action

  • 如果是同步行为会直接通过 Reducers 改变 State

  • 如果是异步行为(副作用)会先触发 Effects 然后流向 Reducers 最终改变 State

Models

  • dva通过一个个的model来管理数据。这些处理数据流的文件统一放在models文件夹下,每一个文件默认导出一个对象,里面包含数据和处理数据的方法

  • model文件的基本结构

export default { // 全局 state 上的 key,必须全局唯一 namespace: 'example', // 初始数据 state: { count: 0, }, // 管理同步方法,必须是纯函数 reducers: { save() { ... }, }, // 管理异步操作,采用了 generator 的相关概念 effects: { *getData() { ... }, }, // 订阅数据源 subscriptions: { setup() { ... }, } }

State

  • State 表示 Model 的状态数据

  • 操作的时候保证每次都是全新对象

Reducer

  • Reducer(也称为 reducing function)函数用于改变State的值。就是描述如何改变数据的方法

  • Reducer必须是纯函数。接受stateaction作为参数,即:(state, action) => state。在函数中更改旧的state,返回新的 state

export default { namespace: 'demo', // ... state: { // ... name: '' }, reducers: { /** * state:旧的state * payload:传过来的数据。第二个参数是action,这个action带有传过来的payload */ save(state, { payload: name }) { // return 新的state,这样页面就会更新 // es6语法,就是把state全部展开,通过后面的payload去覆盖前面的 return ({ ...state, name }) } } }
  • 通过 dispatch函数触发一个 action,来触发Reducer
this.props.dispatch({ type: 'demo/save', payload: name })

Effect

  • Effect 被称为副作用。可以在这里获取你需要的数据,例如向服务器发起一个请求、或是获取其他model里的state

  • 常用来处理异步操作等,基于Redux-saga实现

  • 采用了generator的相关概念,内部使用yield关键字,标识每一步的操作

  • 每一个effect都可以接收两个参数:包含dispatch携带参数payloadaction对象、dva提供的effect函数内部的处理函数集

提供的处理函数:

  • call:执行异步函数。可以理解为等待这个函数执行结束。const data = yield call(doSomethingFunc, parameter);
  • put:发出一个Action,类似于dispatch。这个action既可以是一个reducer也可以是一个effectyield put({ type: 'reducerName', payload: { page } });
  • select:用于获取当前或其他modelstateconst data = yield select(states => states[namespace]);
import * as services from '@/services'; export default { namespace: 'pageModel', state: { title: 'Welcome to Wise.Wrong\'s Bolg', name: 'wise' }, effects: { /** * payload是传来的参数,如果没参数可以写成(_, { call,put,select }) */ *testEffect({ payload }, { call, put, select }) { // 获取state中的值 pageModel就是对应的namespace(唯一) const { name } = yield select(state => state.pageModel); // 接口入参 const params = { name, ...payload }; // services.getInfo是封装好的请求,params是要给这个请求传入的参数 const { data } = yield call(services.getInfo, params); // 请求成功之后,调用reducer同步方法更新state yield put({ // 调用当前 model 的 action 不需要添加 namespace type: 'changeTitle', payload: data, }); } }, reducers: { changeTitle(state, { payload }) { return { ...state, title: payload }; }, }, };

Subscription

  • Subscription语义是订阅,用于订阅一个数据源,然后根据条件dispatch需要的action

  • 数据源可以是当前的时间、服务器的websocket连接、keyboard输入、geolocation变化、history路由变化等

  • 项目中常用于页面初始化数据的自动请求

export default { // ... subscriptions: { // 订阅监听,比如我们监听路由,进入页面就如何,可以在这写 setup({ dispatch, history }) { return history.listen(({ pathname, query }) => { // 进入 '/home' 路由,发起一个名叫 'query' 的 effect if (pathname === '/home') { dispatch({ type: 'query' }); } }); }, }, }

connect

  • connect的作用是将modelcomponent串联起来,绑定StateView
  • @connect函数接收两个参数:
  • 第一个参数接收一个函数,这个函数的参数为statestate是一个全局state变量,需要借助命名空间来指定对应的具体module,这个函数的作用是返回module中的数据,并且绑定到this.props上面,所以最后记得要return
  • 第二个参数也是一个函数,接收dispatch参数,此参数作用是调用model层定义的函数,有返回值,返回值是一个对象,其中的函数可以被this.props调用
// 写法一 - mapStateToProps / mapDispatchToProps import { connect } from 'dva'; function App({ dispatch, user }) { const handleClick = () => { dispatch({ type: 'user/fetchUser' }) } return ( <div> <h2>Hello, {user}</h2> <button onClick={handleClick}>Click me</button> </div> ) } function mapStateToProps(state) { return { user: state.user }; // 返回之后就可以直接这么用 this.props.user } function mapDispatchToProps(dispatch) { return { fetchUser: () => { dispatch({ type: 'user/fetchUser' }) } } // 返回之后就可以直接用 this.props.fetchUser // 如果没有这个方法 就需要这么调用 this.props.dispatch({ type: 'user/fetchUser' }) } // export default connect(mapStateToProps, mapDispatchToProps)(App); export default connect(mapStateToProps)(App);
// 写法二 - 匿名函数 import { connect } from 'dva'; function App({ dispatch, user }) { const handleClick = () => { dispatch({ type: 'user/fetchUser' }) } return ( <div> <h2>Hello, {user}</h2> <button onClick={handleClick}>Click me</button> </div> ) } export default connect(({ user }) => ({ user }))(App);
// 写法三 - ES7修饰器写法 import { connect } from 'dva'; @connect(({ user }) => ({ user })) function App({ dispatch, user }) { const handleClick = () => { dispatch({ type: 'user/fetchUser' }) } return ( <div> <h2>Hello, {user}</h2> <button onClick={handleClick}>Click me</button> </div> ) } export default App;

示例


创作不易,若本文对你有帮助,欢迎打赏支持作者!

 分享给好友: