React 数据管理之 dva 您所在的位置:网站首页 react官网是什么 React 数据管理之 dva

React 数据管理之 dva

2023-11-20 17:16| 来源: 网络整理| 查看: 265

TL;DR

通过本文将了解:

dva 的定位 dva 的创新点及解决的问题 dva 的用法 dva 的源码分析,了解插件系统实现方式 dva 的定位

首先 dva 的定位是基于 redux 和 redux-saga 的数据流管理方案。其次 dva 还提供了一些简化的 API 和易用的插件(例如:集成 react-router、redux-devtool,提供 loading 插件),所以也可以将其理解为小型的应用框架。

dva 的创新点及解决的问题 model 概念提出

dva 最大的创新点是 model 概念的提出。与之前的 Redux 数据管理相比较,dva 提出 model 后解决了以下问题:

之前的 Redux 应用中不仅需要先声明 ActionType,还需要在 reducer 对 ActionType 进行判断。而 dva 提出 model 概念后便没有这样的逻辑了,dva 在内部对 model.reducers 进行了处理。 将数据和操作数据的行为(包括异步逻辑)置于同一个文件中。 通过 redux-saga 实现异步逻辑处理,提出 effects 概念,可以通过 dispatch action 的方式执行 effect 函数。dva 中每个 effect 都是一个 saga,尽管个人认为 redux-saga 并不好用,但是 dva 还是做到了和 redux-saga 高度集成和简化。例如:每个 effect 可以通过数据的第二个值指定 saga 的类型,在 dispatch action 执行一个 effect 时其返回值是 promise。 可以通过实现返回 model 的函数快速复用一类 model(如:dva-loading model)。 实现 unmodel 和 replaceModel

redux 作为全局数据管理方案,最大的弊端就是随着页面的不断增加,store 将不断膨胀。如果要在页面切换过程中,自己实现卸载旧页面数据、挂载新页面数据和保留公共数据,又将非常麻烦。

所以 dva 在 model 概念的基础上,可以方便的添加、卸载、更新一个 model,可以非常方便地动态修改 store。

框架级优化,提升开发体验

开发者使用 dva 后只需关心如何开发 model 和页面,其他繁琐的 redux 相关的配置都被隐藏在 dva 内部。

使用 connected-react-router 将 router 状态存放到 redux 中。在没有 Hooks 的时代里,路由改变触发组件重绘需要用 将组件包裹起来才行。但将 router 的状态存放到 redux 中后,路由改变就跟其他状态改变没什么区别了。 开发者通过 app.router(({ app, history }) => ) 和 app.start(rootDom) 关注页面开发,而无需关心如何把 Redux 如何挂载到组件树中。 默认集成 Redux DevTool。 可 动态加载 model 和组件。在 model 加载完成后将他们注册到 app 中,最后 resolve 的是组件。在组件和 model 没有加载之前,会展示 Loading 态组件。 内部做了处理,可在服务端使用。比如:同构的 fetch 函数和动态加载 react-dom 等。 其他创新点(或缺点) model.subscriptions 用于订阅一些全局事件。这个创新我感觉没什么用,因为我们通常都是在组件的生命周期钩子中订阅事件。现在把订阅操作移到 model 中,不但没有必要,而且它还不能覆盖所有的订阅场景(如:只订阅组件内的 dom 节点触发的事件),所以基本没什么用。唯一的区别就是其订阅函数和取消订阅函数可以在 model 的生命周期中自动执行,比如 app.unmodel(namespace) 时自动执行 namespace 对应 model 的取消订阅函数。 使用 redux-saga 作为异步逻辑处理方案,有以下缺点: 学习成本变高了(包括 saga 和 generator function 的用法) effects 中可以调用其他 model 的 reducers 和 effects,这就说明一个 model 耦合了另一个 model,最后会导致 effects 中的代码变混乱。 在 effects 中会写出 yield yield put({ type: 'effectB', payload: xxx }) 代码,该代码非常难以理解,其目的是等待 effectB 函数结束后再继续执行。 redux-saga 的核心点在于通过 generator function 测试异步逻辑,但是这种测试方式非常依赖代码实现,没有意义。 effect 中的报错,需要在 dva 的 onError 中做 fallback 处理,否则错误会被吃掉。 dva 的用法 1. 创建 dva 实例 app 变量,可以传入 history 指定路由形式 import dva from "dva" import { createBrowserHistory } from "history" const app = dva({ history: createBrowserHistory() }) 2. 通过 app.router() 和 app.start() 启动应用 import React from "react" import "./index.css" import dva from "dva" import { Router, Route, Switch } from "react-router-dom" import { createBrowserHistory } from "history" const app = dva({ history: createBrowserHistory() }) app.router(({ history }) => { return ( a b ) }) app.start(document.getElementById("root")) 3. 编写 UI 组件、定义 Model、connect 起来

这几步的使用方式参考 dva 官方文档即可:

编写 UI 组件 定义 Model connect 起来 dva 的源码分析

个人愚见,dva 的源码还是比较脏的,代码实现相当灵活,不建议细究。例如:unmodel 方法中修改函数的实参,而该实参 reducers 又是个全局变量,在下一个函数 createReducer() 中使用,。所以整体来看,代码比较晦涩。

dva 分为 4 个 packages:

dva-core 是 dva 数据管理方案的核心代码。 dva 是暴露给开发者使用的 package。其在 core 的基础上,将 router 集成到 redux 中,简化 react-redux 的接入,将常用的底层方法都暴露给开发者。可以认为是框架特性,提升开发体验的一层。 dva-immer 使用 _handleActions Hook 实现,实现在 reducer 方法中可以直接修改 state 的状态。 dva-loading 用于在 Effect 执行前触发 loading 态,在 Effect 执行后关闭 loading 态,内部通过 onEffect 和 extraReducers Hook 实现。其状态包含 global、model、effects 三个维度,通用使用 global 即可。使用方式参考测试用例代码。 dva 中插件系统的实现

参考源码文件,dva 的插件系统分为两个部分:

插件注册 use(plugin)。 返回最终的插件 get(key)。 插件注册

每个插槽都维护一个数组,每次注册插件就是向该数组中添加插件。

function use(plugin) { const { hooks } = this for (const key in plugin) { if (Object.prototype.hasOwnProperty.call(plugin, key)) { if (key === "_handleActions") { // 特殊项 this._handleActions = plugin[key] } else if (key === "extraEnhancers") { // 特殊项 hooks[key] = plugin[key] } else { // 常规项,将 `plugin` 注册到 `hooks` 数组中 hooks[key].push(plugin[key]) } } } } 返回最终的插件

返回最终的插件也比较简单,直接返回 key 对应的所有插件即可。

function get(key) { const { hooks } = this if (key === "extraReducers") { // 特殊项 return getExtraReducers(hooks[key]) } else if (key === "onReducer") { // 特殊项 return getOnReducer(hooks[key]) } else { // 返回 `key` 对应的所有插件 return hooks[key] } } function getExtraReducers(hook) { let ret = {} for (const reducerObj of hook) { ret = { ...ret, ...reducerObj } } return ret } function getOnReducer(hook) { return function(reducer) { for (const reducerEnhancer of hook) { reducer = reducerEnhancer(reducer) } return reducer } } 插件系统总结

整体来看 dva 基本是通过回调函数实现插件机制,比较简单粗暴。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有