Dva中实现异步effect的原理分析

1,849 阅读4分钟

Dva简单介绍

首先,Dva是基于现有应用框架(redux+react-router+redux-saga等)的一层轻量级封装框架,简化了redux和redux-saga的使用。

相关概念介绍

model

Dva采用redux作为数据流,并且将redux的创建初始化store、创建定义reducer等操作集合到model中进行统一的管理,避免了redux中每个操作都是一个文件,导致项目文件过多、不好管理的问题。model中一般包含几个部分:

  • namespace:model的名字,用来在页面中找到要调用的那一个model,类似于id。
  • state:用来存放数据的地方,和redux中的store一样,是不可变数据(immutable data),保证每次都是全新的对象,没有引用关系。
  • reducers:reducer是唯一可以更新state的地方,通过actions中传入的值,与当前reducers中的值进行运算获得新的值,也就是新的state。reducer是一个纯函数。
  • effect:Action 处理器,处理异步动作,基于redux-saga实现。
  • subscription:订阅,用来订阅一个数据源,然后根据条件,dispatch出对应的action。

dva实现异步的源码分析

Dva封装了redux-saga,并采用redux-saga实现异步effect。分析Dva如何使用redux-saga来实现异步effect,需要从Dva的源码进行分析。

首先,我们可以从官网下载Dva源码( github.com/dvajs/dva )。我们知道Dva有两个重要的模块:dva和dva-core,接下来我们会分析一下这两个模块分别做了什么。(文中使用Dva代表dva框架,dva表示框架中的dva模块)

dva做了什么

首先是dva,从源码中dva/src/index.js可以看到,

image.png

简单来说,dva主要做了三件事:

  1. 调用从dva-core中引入的create方法实例化一个app
  2. 重写了App实例的router方法(添加了 redux 的中间件 react-redux-router,强化了 history 对象的功能)
  3. 重写了App实例的start方法(使用 call 给 dva-core 实例化的 app(这个时候还只有数据层) 的 start 方法增加了一些新功能:使用react-redux的Provider组件将数据与视图联系起来) dva/src/index.js主要实现了 dva 的 view 层,同时传递了一些初始化数据到 dva-core 所实现的 model 层。而 dva-core 主要解决了 model 的问题,包括 state 管理、数据的异步加载、订阅-发布模式的实现。

dva-core做了什么

我们知道在 dva/src/index.js里,调用 dva-core中的 create方法创建了一个 app 对象,并传递了传递 opts 和 createOpts两个参数。其中 opts 是使用者添加的控制选项,createOpts可以理解为初始化的配置。

而在 dva-core/src/index.js中,便是这个 app 对象的具体创建过程以及包含的方法:

image.png

上图清晰的展示了dva-core的主要内容:创建一个包含model的app对象,同时这个app中也会包含一些插件功能和一些方法(use,model,start等)

model方法

在dva/src/index.js并没有对于app的model方法做任何处理,所以我们可以理解为:dva中的model就是dva-core中定义的model方法。

image.png

prefixNamespace 原理是使用 reduce (数组的递归)对每一个 model 做处理,为 model 的 reducers 和 effects 中的方法添加了 ${namespace}/ 的前缀。

这里还有对应的unmodel方法,用于注销对应的model。如图我们可以看到,方法中依次删除store里对应的namespace下的reducers,并通过发送type为${namespace}/@@CANCEL_EFFECTS的请求来取消异步的effects(这里在getSaga方法中出现),取消对应的订阅以及最后从app的models中删除该namespace的model。

image.png

start方法

start 方法是 dva-core 的核心,在 start 方法里,Dva 完成了 store 初始化 以及 redux-saga 的调用,也是本文的核心内容,即Dva如何实现异步。

首先,在start方法中定义了onError方法,用来进行全局错误处理。

sagaMiddleware

接下来,调用 redux-saga 中提供的方法 createSagaMiddleware()创建了 saga中间件,如何将saga中间件应用到store中?Dva采用了动态的调用方法:sagaMiddleware.run()

image.png

因此,我们缩略出start方法的主要过程就是:

image.png

创建一个saga中间件,遍历循环app里的model,如果model里存在effects,则把model和model.effects传入到app._getSaga方法中,并把结果存入sagas数组中。而app._getSaga = getSaga.bind(null);所以接下来我们看一下getSaga方法:

image.png

图中,我们可以看到,getSaga方法最终返回的是一个 generator函数,这个函数首先遍历了当前 model中的 effects里的所有方法,并对每一个方法创建一个watcher,并使用 saga 函数的 fork 将该函数切分到另一个单独的线程中去(生成了一个 task 对象)。同时为了方便对该线程进行控制,在此 fork 了一个 generator 函数。在该函数中拦截了取消 effect 的 action,即拦截发送出来的type为${namespace}/@@CANCEL_EFFECTS的请求(同unmodel中取消异步的原理),一旦监听到则立刻取消分出去的 task 线程。

总结

以上就是Dva如何利用redux-saga来控制model中的effects实现异步的源码分析,dva-core中的start方法中不仅实现了利用redux-saga实现异步,同时还实现了model中的store、reducers以及subscription的创建,可以说dva-core模块让Dva框架能够在model里集成redux的数据处理流程。