软件分层

首先,我们需要简单了解一下软件架构的「分层」理念。

一个完整的软件,往往会被拆分成多个不同的层次,每一个层次聚焦于完成特定的功能。

使用 model - 图1

上图中,左侧是服务端代码的层次结构,由 Controller、Service、Data Access 三层组成服务端系统:

  1. Controller 层负责与用户直接打交道,渲染页面、提供接口等,侧重于展示型逻辑
  2. Service 层负责处理业务逻辑,供 Controller 层调用。
  3. Data Access 层顾名思义,负责与数据源对接,进行纯粹的数据读写,供 Service 层调用。

上图的右侧是前端代码的结构,同样需要进行必要的分层:

  1. Page 负责与用户直接打交道:渲染页面、接受用户的操作输入,侧重于展示型交互性逻辑
  2. Model 负责处理业务逻辑,为 Page 做数据、状态的读写、变换、暂存等。
  3. Service 负责与 HTTP 接口对接,进行纯粹的数据读写。

引入 DVA

在 umi 中,你可以通过在插件集 umi-plugin-react 中配置 dva 打开 dva 插件。

  1. export default {
  2. plugins: [
  3. ['umi-plugin-react', {
  4. antd: true,
  5. dva: true,
  6. }],
  7. ],
  8. // ...
  9. }

Model 是前端分层中的腰部力量,承上启下,负责管理数据(状态)。业界主流的状态管理类库有 reduxmobx,等。在我们的教程中,则使用 DVA 框架承担这一角色。

DVA 是基于 redux、redux-saga 和 react-router 的轻量级前端框架及最佳实践沉淀。其中,model 是 DVA 中最重要的概念,一个简单的 model 示例如下:

  1. app.model({
  2. namespace: 'todoList',
  3. state: [],
  4. effects: {
  5. *query({ _ }, { put, call }) {
  6. const rsp = yield call(queryTodoListFromServer);
  7. const todoList = rsp.data;
  8. yield put({ type: 'save', payload: todoList });
  9. },
  10. },
  11. reducers: {
  12. save(state, { payload: todoList }) {
  13. return [...state, todoList];
  14. },
  15. },
  16. });

DVA 的 model 对象有几个基本的属性,需要大家了解。

  1. namespace:model 的命名空间,只能用字符串。一个大型应用可能包含多个 model,通过namespace区分。
  2. state:当前 model 状态的初始值,表示当前状态。
  3. reducers:用于处理同步操作,可以修改 state,由 action 触发。reducer 是一个纯函数,它接受当前的 state 及一个 action 对象。action 对象里面可以包含数据体(payload)作为入参,需要返回一个新的 state。
  4. effects:用于处理异步操作(例如:与服务端交互)和业务逻辑,也是由 action 触发。但是,它不可以修改 state,要通过触发 action 调用 reducer 实现对 state 的间接操作。
  5. action:是 reducers 及 effects 的触发器,一般是一个对象,形如{ type: 'add', payload: todo },通过 type 属性可以匹配到具体某个 reducer 或者 effect,payload 属性则是数据体,用于传送给 reducer 或 effect。

上面这些概念,初学者会觉得比较抽象,这是正常的。大家可以通过后面的例子,慢慢体会。DVA 的文档非常优秀,建议大家直接学习,参考链接:

  1. Dva 概念
  2. Dva 图解