给移动端配置dva吧,真的很香~

1,348 阅读4分钟

这是我参与「掘金日新计划 · 4 月更文挑战」的第17天,点击查看活动详情

大家好,我叫小杜杜,今天来讲讲一个状态库dva,我首次接触dva还是Ant Design Pro V4中接触的,相比于redux简单了许多,上手也更加容易,接下来介绍下dva

什么是dva

dva 是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch

Model

要学习dva,我们首先要了解dva的属性流动:

其中包含五个属性,分别是 namespacestateeffectsreducerssubscription

namespace 命名空间

  • model的命名空间,同时namespace也是在全局state上的属性,不支持通过.的方式创建多层命名空间。
  • 类型为 string
  • namespace 具有 唯一性

state 初始数据

  • state 是整个 Model 的 数据层
  • 应用的 state 被存储在一个 object tree 中
  • 应用的 初始 state 在 model 中定义。也就是说 由 model state 组成全局的 state
  • 操作的时候每次都要当作不可变数据(immutable data)来对待,保证每次都是全新对象,没有引用关系,这样才能保证 State 的独立性,便于测试和追踪变化。

effects 副作用

  • 以key/value格式定义effect。用于处理异步操作和业务逻辑,不直接修改state
  • 由action触发,可以触发 action,可以和服务器交互,可以获取全局 state 的数据等等。

reducers

  • 以key/value的格式来定义reducer。用于处理同步操作,唯一可以修改state的地方,由action触发。
  • 格式为:(state, action) => newState 或者 [(state, action) => newState, enhancer]
  • 他接收参数 state 和 action,返回新的 state, 通过语句表达即 (state, action) => newState,所以不存在自加操作。该函数把一个集合归并成一个单值。
  • Reducer 的概念来自于是函数式编程,在 dva 中,reducers 聚合积累的结果是当前 model 的 state 对象。
  • 通过 actions 中传入的值,与当前 reducers 中的值进行运算获得新的值(也就是新的 state)。
  • Reducer函数必须是纯函数

移动端的配置

在我们配置项目的时候,只需要加入 dva-core dva-loading这两个插件即可

执行命令:

  • yarn add react-redux -S
  • yarn add dva-core dva-loading -S

另外,由于是移动端,我们常常会配置数据持久化,这个主要是防止用户强制刷新页面,导致dva数据的清空所影响的~

通过dva-model-persisit 就可以实现

引入的时候可选本地存储或会话存储,这里建议使用会话存储

  • 本地存储 dva-model-persist/lib/storage
  • 会话存储 dva-model-persist/lib/storage/session

创建 dva.js

首先我们需要创建两个方法,一个是 createApp, 一个是 getDispatch

  • createApp 的作用就是将 数据注入到 model,然后设置 store
  • getDispath 的作用就是将 dispatch 导出,让其有操作的方法
import { create } from 'dva-core'
import createLoading from 'dva-loading'
import { persistEnhancer } from 'dva-model-persist';
import storage from 'dva-model-persist/lib/storage/session';

let app
let store
let dispatch
let registered

function createApp(opt) {
  opt.onAction = []
  app = create(opt)
  app.use(createLoading({}))
  app.use({ //配置本地存储
    extraEnhancers: [
      persistEnhancer({
          key: 'model',
          storage
      })
    ],
  })
  // 注入model
  if (!registered) {
    opt.models.forEach((model) => app.model(model))
  }
  registered = true
  app.start()

  // 设置store
  store = app._store
  app.getStore = () => store
  app.use({
    onError(err) {
      console.log(err)
    },
  })

  // 设置dispatch
  dispatch = store.dispatch
  app.dispatch = dispatch

  return app
}

export default {
  createApp,
  getDispatch() {
    return app.dispatch
  },
}

设置models

这个就是项目中放置 model 的部分了, 只要将其对应的导出即可

import app from './app'

export default [
  app,
]

集中配置

准备好上面两个模块,就可以正式配置了,要配置根目录文件,也就是 App.jsx

只需要将 models 全部传入到 dva 中,在将值传入 Provider 中的 store 就行了,当然 Provider 需要将里面的所有组件进行包裹,这样就 ok 了

import { Provider } from 'react-redux'
import models from './models'
import { Router, dva } from './utils';

const app = dva.createApp({
  models,
})

const store = app.getStore()

const App = () => {
  return (
    <Provider store={store}>
      ...
    </Provider>
  );
}

export default App;

effects 需要注意的问题

我们先来看看一个effect

  effects: {
    *setAdd(data, {call, put, select}){
     
    },
  },

effects中有三个参数,分别是 call putselect,分别介绍一下

  • call: 用于异步调用,支持promise
    request: 请求的接口
    payload:请求的参数

    const res = yield call(request, payload })
  • put: 触发 action (也就是走向 reducers, 通过 type判断)
    yield put ({
       type:'jj', // 在此模块的
       //type:'xx/jj', // 可以是不同类型的, 
       payload: data // 所需要的参数
    });
  • select: 用于从 state中获取数据
    //state:代表所有models数据, state.data 代表所需要的数据
    const data = yield select(state=>state.data);

此外,还有一个yield,他的作用主要是保证当前语句执行完毕后,再执行下面的代码,并且需要和*配合使用。

这个语法与生成器 Generator 的写法非常相似,同时也可以理解为就是async await

这里需要注意:

  • namespace 命名空间不能重复
  • effects 可以触发不同模块的 reducers,也就是说所有的模块都是可以的,不限于此模块

End

致此,在移动端配置dva就已经讲完了,有兴趣的小伙伴可以自己配置一下,打造开箱即用的 react 移动端框架,相信这篇文章一定能更好的帮助到各位~