本文会介绍一下 dva 的相应知识点,使用 dva 的流程,以及使用 dva 中的坑。
并会通过 dva-cli 和 create-umi 写一个 倒计时计数器 的例子。
希望通过这篇文章能让你大致了解 dva 的使用流程。
Dva 简介
- 借鉴 elm 的概念,Reducer,Effect 和 Subscription
- 框架,而非类库
- 基于 redux,react-router,redux-saga 的轻量级封装
Dva 的特性
- 仅有 5 个 API,其用法我们会在之后详细介绍。
- 支持 HMR,支持模块的热更新。
- 支持 SSR (ServerSideRender),支持服务器端渲染。
- 支持 Mobile/ReactNative,支持移动手机端的代码编写。
- 支持 TypeScript,支持 TypeScript,毋庸置疑这个会是 Javascript 的一个趋势。
- 支持路由和 Model 的动态加载。
Dva 的 5 个API
app = dva(Opts)
创建应用,返回 dva 实例(注:dva 支持多实例)。
在 opts 可以配置所有的 hooks
const app = dva({
history,
initialState,
onError,
onAction,
onStateChange,
onReducer,
onEffect,
onHmr,
extraReducers,
extraEnhancers,
});
这里比较常用的是,history 的配置,一般默认的是hashHistory,如果要配置 history 为 browserHistory,可以这样:
import createHistory from 'history/createBrowserHistory';
const app = dva({
history: createHistory(),
});
- 关于
react-router中的hashHistory和browserHistory的区别大家可以看:react-router。 initialState:指定初始数据,优先级高于model中的state,默认是{},但是基本上都在model里面设置对应应的state。
app.use(Hooks)
配置 hooks 或者注册插件。
这里最常见的就是 dva-loading 插件的配置,
import createLoading from 'dva-loading';
// ...
app.use(createLoading(opts));
但是一般对于全局的 loading 我们会根据业务的不同来显示相应不同的 loading 图标,我们可以根据自己的需要来选择注册相应的插件。
app.model(ModelObject)
这个是你数据逻辑处理,数据流动的地方。
modal 是 dva 里面与我们真正进行项目开发,逻辑处理,数据流动的地方。
这里面涉及到的 namespace、Modal、effects 和 reducer 等概念都很重要,我们会在接下来的部分详细讲解。
app.router(Function)
注册路由表,我们做路由跳转的地方。
一般都是通过下面的方式编写:
import { Router, Route } from 'dva/router';
app.router(({ history }) => {
return (
<Router history={history}>
<Route path="/" component={App} />
<Router>
);
});
但是如果你的项目特别的庞大,我们就要考虑到相应的性能的问题,但是入门可以先看一下这个。
解决性能问题,其实就是对路由进行按需加载,实现起来也比较简单,就是使用了 require.ensure 这个方法来帮我们实现,同时我们也可以使用 ES6 module 的动态 import 来实现按需加载。不过动态 import 这个方法还在提案中,我们需要接着 babel 插件来帮我们实现,具体笔者就不展开了。
app.start([HTMLElement], opts)
启动应用,将我们的应用跑起来。
Dva 九个概念
State(状态)
初始值,我们在 dva() 初始化的时候和在 model 里面的 state 对其两处进行定义,其中 model 中的优先级低于传给 dva() 的 opts.initialState。
如下:
// dva()初始化
const app = dva({
initialState: { count: 1 },
});
// modal()定义事件
app.model({
namespace: 'count',
state: 0,
});
Action
表示操作事件,可以是同步,也可以是异步
action 的格式如下,它需要有一个 type ,表示这个 action 要触发什么操作;payload 则表示这个 action 将要传递的数据
{
type: String,
payload: data,
}
我们通过 dispatch 方法来发送一个 action
// Action
// Action 表示操作事件,可以是同步,也可以是异步
{
type: String,
payload: data
}
// 格式
dispatch(Action);
dispatch({ type: 'todos/add', payload: 'Learn Dva' });
其实我们可以构建一个 Action 创建函数,如下
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
//我们直接dispatch(addTodo()),就发送了一个action。
dispatch(addTodo())
action 更多内容可以查看文档:redux——action
Model
model 是 dva 中最重要的概念,Model 非 MVC 中的 M,而是领域模型,用于把数据相关的逻辑聚合到一起,几乎所有的数据,逻辑都在这边进行处理分发
state
这里的 state 跟我们刚刚讲的 state 的概念是一样的,只不过它的优先级比初始化的低,但是基本上项目中的 state 都是在这里定义的。
namespace
model 的命名空间,同时也是他在全局 state 上的属性,只能用字符串,我们在发送 action 到相应的 reducer 时,就会需要用到 namespace 。
Reducer
以 key/value 格式定义 reducer,用于处理同步操作,唯一可以修改 state 的地方。由 action 触发。其实一个纯函数。
Effect
用于处理异步操作和业务逻辑,不直接修改 state,简单的来说,就是获取从服务端获取数据,并且发起一个 action 交给 reducer 的地方。
其中它用到了redux-saga,里面有几个常用的函数。
*add(action, { call, put }) {
yield call(delay, 1000);
yield put({ type: 'minus' });
},
在项目中最主要的会用到的是 put 与 call。
Subscription
subscription 是订阅,用于订阅一个数据源,然后根据需要 dispatch 相应的 action。
在 app.start() 时被执行,数据源可以是当前的时间、当前页面的 url、服务器的 websocket 连接、history 路由变化、键盘事件等等。
Router
Router 表示路由配置信息,项目中的 router.js。
export default function({ history }){
return(
<Router history={history}>
<Route path="/" component={App} />
</Router>
);
}
RouteComponent
RouteComponent 表示 Router 里匹配路径的 Component,通常会绑定 model 的数据。如下:
import { connect } from 'dva';
function App() {
return <div>App</div>;
}
function mapStateToProps(state) {
return { todos: state.todos };
}
export default connect(mapStateToProps)(App);
整体架构
我简单的分析一下这个图:
首先我们根据
url 访问相关的 Route-Component,在组件中我们通过 dispatch 发送 action 到 model 里面的 effect 或者直接 Reducer。
当我们将 action 发送给 Effect,基本上是取服务器上面请求数据的,服务器返回数据之后,effect 会发送相应的 action 给 reducer,由唯一能改变 state 的 reducer 改变 state ,然后通过 connect 重新渲染组件。
当我们将 action 发送给 reducer,那直接由 reducer 改变 state,然后通过 connect 重新渲染组件。
这样我们就能走完一个流程了。
umi与 dva 和 doadhog 的关系
以下比较引自官网:
roadhog是基于webpack的封装工具,目的是简化webpack的配置umi可以简单地理解为roadhog+ 路由,思路类似next.js/nuxt.js,辅以一套插件机制,目的是通过框架的方式简化React开发dva目前是纯粹的数据流,和umi以及roadhog之间并没有相互的依赖关系,可以分开使用也可以一起使用,更多可以参考:使用 umi 改进 dva 项目开发
参考链接
- umi 官网
- roadhog介绍
- dva 官网
- webpack4.0 学习文档
- Dva-React 应用框架在蚂蚁金服的实践
- 10分钟 让你dva从入门到精通
- 基于dva-cli&antd的react项目实战