当前位置: 首页 > 工具软件 > dva > 使用案例 >

dva-core的基本使用

乐刚毅
2023-12-01

每天对自己多问几个为什么,总是有着想象不到的收获。 一个菜鸟小白的成长之路(copyer)

dva定义

dva 是一个基于 redux 和 redux-saga 的数据流方案

redux-saga中的几个辅助函数

take: 监听action动作,会阻塞后面的代码执行。action被触发后,后面的代码才能执行。(阻塞函数) take(pattern)
put: 相当于redux的dispatch,用来触发action 的 effect,然后再reducer中接受。 (阻塞函数) put(action)
call: 调用其他的函数,相当于jS中的call方法,其实用法也是差不多的。注意: fn 函数可以是一个 Generator 函数,也可以是一个返回 Promise 的普通函数 (阻塞函数) call(fn, ...args)
fork: 跟call函数基本相似,都是调用其他函数(不是阻塞函数) fork(fn, args)
select: 获取整个store中的state,相当于 store.getState()
cancel: 一旦任务被 fork,可以使用 yield cancel(task) 来中止任务执行。(只是针对fork,没有阻塞性)

// take
// 监听type为ADD的action执行,直到这个action被触发了,才会执行下面的代码
yield take('ADD'); 
console.log(12323)

// put
// 触发reducer中的type为ADD,然后返回新的state
yield put({
  type: 'ADD',
  ...payload
})

// call 
export const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
yield call(delay, 500)
//等待上面的delay函数执行完,才调用下面的代码
console.log(123)

// fork 
export const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
yield fork(delay, 500)
//不用等待上面的delay函数执行完,直接会调用下面的代码
console.log(123)

//selecct
let tempList = yield select(state => state.getTodoList.list)  // state就是store的整体的state

数据流向

用户需要改变数据的时候,需要通过dispatch发起一个action,同步操作会直接通过Reducers改变State;
如果是异步操作会先触发Effects然后流向Reducers最终改变State。

安装

这里只是针对dva-core

// js
npm install dva-core

// ts
npm install dva-core-ts

创建store

// store.ts

import { create } from 'dva-core-ts'
import models from '../models'
const app = create()
// 依次注册nodel
models.forEach((model: any) => {
  app.model(model)
})
// 启动引用,必须在注册之后,在获取store之前
app.start()
// 获取store
const store = app._store;
export default store

app的类型

如果不想看类型分析,就直接看下面的代码使用(虽然是用ts写的,但是js的写法也是一样的)

export function create(hooksAndOpts?: DvaOption, createOpts?: CreateOpts): DvaInstance;
// 返回一个DvaInstance的类型
export interface DvaInstance {
  use: (hooks: Hooks) => void;   // 注册hook在应用上
  model: (model: Model) => void;  // 注册model
  unmodel: (namespace: string) => void;   // 卸载model
  start: () => void;    // 启动引用
  _store: Store;        // 拿去整个引用的store
}

把store注册到项目中

import { Provider } from 'react-redux'
import store from './store'

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

接下来,就是主要定义model,

定义model

一个model都有完整的结构,statereducereffectnamespace
effect 是用来处理异步的请求,然后把数据传递给reducer,然后再修改state (处理异步代码)
reducer 是用来修改state的唯一渠道
state 就是用来保存数据的地方
namespace 用来标识唯一性
这个跟vuex中的思想很相似。

model的类型

export interface Model {
  namespace: string;
  state?: any;
  reducers?: ReducersMapObject | ReducersMapObjectWithEnhancer;
  effects?: EffectsMapObject;
  subscriptions?: SubscriptionsMapObject;
}

使用

import { Model } from 'dva-core-ts'

state可以保存任何的数据,所以使用any (后面可以自定义类型)

分析reducers的类型

export type Reducer<S = any, A extends Action = AnyAction> = (
  state: S | undefined,
  action: A
) => S

export type ReducersMapObject<S = any, A extends Action = Action> = {
  [K in keyof S]: Reducer<S[K], A>
}

ReducersMapObject 是一个对象,对象的类型主要就是 Reducer类型,该接受一个泛型,传递的类型就是state的类型

使用

import { Reducer } from 'redux'
interface ICount {
  count: number;
}
// 定义一个reducer函数
add: Reducer<ICount>

effect的类型分析

export interface EffectsMapObject {
  [key: string]: Effect | EffectWithType;
}

export type Effect = (action: AnyAction, effects: EffectsCommandMap) => void;
export type EffectType = 'takeEvery' | 'takeLatest' | 'watcher' | 'throttle';
export type EffectWithType = [Effect, { type: EffectType }];

// 针对action
export interface Action<T = any> {
  type: T
}
export interface AnyAction extends Action {
  [extraProps: string]: any
}
// 总结: 简单来说,action中type是必填字段,其他的都是可key-value对应的

// 针对effects 
export interface EffectsCommandMap {
  put: typeof put;
  call: typeof call;
  select: typeof select;
  take: typeof take;
  cancel: typeof cancel;
  [key: string]: any;
}
// 就是redux-saga中的几个函数

使用:

import { Effect } from 'dva-core-ts'

subscriptions类型分析

export interface SubscriptionsMapObject {
  [key: string]: Subscription;
}

export type Subscription = (api: SubscriptionAPI, done: Function) => void;

export interface SubscriptionAPI {
  history: History;  // 用来监听路由跳转
  dispatch: Dispatch<any>;
}

使用

  • subscriptions 中配置的key的名称没有任何约束,而且只有在app.unmodel的时候才有用。
  • subscriptions 中配置的只能dispatch所在model的reducer和effects
  • subscriptions 中配置的函数只会执行一次,也就是在调用 app.start() 的时候,会遍历所有 model 中的 subscriptions 执行一遍。
  • subscriptions 中配置的函数需要返回一个函数,该函数应该用来取消订阅的该数据源。
subscriptions: {    
    setup({ dispatch, history }) { // 路由变化,
      console.log(history);
    },
  },

model的具体使用

import { Model, Effect, SubscriptionsMapObject } from 'dva-core-ts'
import { Reducer } from 'redux'

// 定义state的类型
interface ICount {
  count: number;
}

// 定义model的类型
export interface countModel extends Model {
  namespace: 'count',   
  state: ICount,
  effects: {
    addAsync: Effect,
    subAsync: Effect,
  },
  reducers: {
    add: Reducer<ICount>,
    sub: Reducer<ICount>
  },
  subscriptions: SubscriptionsMapObject
}

// 逻辑代码

export const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

const initState: ICount = {
  count: 10
}

// 定义一个model,类型为countModel
const countModel: countModel = {
  namespace: 'count',
  state: initState,
  reducers: {
    add: (state, action) => {
      return {...state, count: state?.count + action.payload.num}
    },
    sub: (state, action) => {
      return {...state, count: state?.count as number - action.payload.num}
    },
  },
  effects: {
    *addAsync({payload}, {call ,put}) {
      yield call(delay, 2000)
      yield put({
        type: 'add',
        payload,
      })
    },
    *subAsync({payload}, {call ,put}) {
      yield call(delay, 2000)
      yield put({
        type: 'sub',
        payload,
      })
    }
  },
  subscriptions: {    
    setup({ dispatch, history }) { 
      console.log(history);
    },
  },
}

export default countModel

集中model的处理

// model.ts
import CountModel from './count'
export default [CountModel]

在组件中的使用store

dva-core-ts文件的导出

export {
  connect,
  connectAdvanced,
  useSelector,   // 拿去state
  useDispatch,   // dispatch,修改state的值
  useStore,
  DispatchProp,
  shallowEqual,
} from 'react-redux';

组件使用

import { useSelector, useDispatch } from 'dva-core-ts'

const CountCom = () => {
  // 获取dispatch
  const dispatch = useDispatch();
  // 拿取值
  const countReducer = useSelector((state: any) => {
    return state.count
  })

  return (
    <div>
       <h3>count的值: {countReducer.count}</h3>
        {/* 触发effects中的函数(异步修改)*/}
       <Button onClick={() => dispatch({type: 'count/addAsync', payload: {num: 1}})}>加</Button>
        {/* 触发reducer中的函数(同步修改)*/}
       <Button onClick={() => dispatch({type: 'count/sub', payload: {num: 1}})}>减</Button>
    </div>
  )
}

总结

dva-core的使用,跟vuex的用法还是很相似的,用来还是比较的简单。
主要就是要理解:

  • 生成器的基本使用
  • redux-saga的几个函数
  • dva的思想(vuex)

上面还有一个subscriptions 还没有使用,主要没有具体的使用,可能是在实际中没使用吧,不是很熟悉,记住有这一点就行了,偷笑!!!

上面的代码或者解释有错误的,欢迎指正。

 类似资料: