umidva都是阿里出的简化react开发的架构,以前都是直接开发的没使用这套,后来接触下来以后发现,开发感觉挺舒适的,刚开始使用的dva,有一个缺陷就是基于roadhog搭建的webpack架子,给我们自定义的接口实在有限,后来umi加入了webpack-chain可以自己添加、修改webpack的配置,值得一试。

umi+dva 的架构中,dva被当成了数据流,集合了redux/redux-saga,处理异步操作,而且还有个方便的是集合了dva-loading这个插件,不用我们去手动的写loading状态的show/hide

dva

dva 首先是一个基于 reduxredux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-routerfetch,所以也可以理解为一个轻量级的应用框架。
我们只是用他集合reduxredux-saga的部分,其他的我们使用umi的就行,dva 将文件分为了modelservicesmodel文件夹下建立的就是我们的 redux 文件,services下建立的是我们的api接口,我们看一下dvamodel文件

models/login.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import { queryLogin } from "../services/login"; //导入loginService API 接口
import { Toast } from "antd-mobile";

export default {
// namespace 用来标识reducer文件,connect的时候使用
// 类似redux整合所有reducer文件中的key值
namespace: "login",

state: {
//相当于redux的InitialState
login: ""
},

subscriptions: {
//可以在这里进行监听
},
//这里进行的就是异步操作了,集成了 redux-saga
effects: {
*fetch({ payload, callback }, { call, put }) {
// eslint-disable-line
try {
const response = yield call(queryLogin, payload);
if (response.code === "0000") {
yield put({ type: "save", payload: response.data });
} else {
const errorObj = new Error();
errorObj.msg = response.data.msg;
throw errorObj;
}
} catch (e) {
console.log(new Error(e));
Toast.fail(e.msg || "登录请求失败,请重试");
}
}
},
// redux的 reducer 函数
reducers: {
save(state, action) {
return { ...state, ...action.payload };
}
}
};

在组件中使用dvamodel,只显示主要代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class Login extends Base {
login = async () => {
const { dispatch, form } = this.props;
form.validateFields(async (err, fieldsValues) => {
let { phone, password } = fieldsValues;
if (err) {
return handleError(err, fieldsValues);
} else {
await dispatch({
type: "login/fetch",
payload: {
user_no: phone,
user_pass: password
});
}
});
};

render() {
const {
loading: { effects }
} = this.props;
return (
<div>
...
<WingBlank className={styles.logbox}>
<Button
type="primary"
className={styles.login}
onClick={() => {
this.login();
}}
>
登录
</Button>
</WingBlank>
<ActivityIndicator
toast
text="登录中..."
animating={effects["login/fetch"] || false}
/>{" "}
</div>
);
}
}

export default connect(({ login, loading }) => ({
login,
loading
}))(createForm()(Login));
  • 首先和redux一样我们需要connect组件注入你需要使用的model这里我们注入了loginloading
  • connect注入了login模块后,点击登录时,会调用 login/fetch的异步操作(调用 loginModels 下面的 fetch 方法),如此完成一次完整的流程操作
  • 这个组件中我们可以看到dva-loading的使用,用户在点击登录后会调用login中的fetch异步操作,在dva-loading中就会自动监听这个 effects 直到他完成,我们就可以直接判断effects[loing/fetch]来监听这个异步操作是否完成,十分的方便

umi

对与umi来说,最大的优势就是采用了 next.js 的约定式路由,文件目录即路由, 省却了我们自己去配置路由了,对与之前我们的路由这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const RouteConfig = [
{
path: "/",
component: Index,
exact: true
},
{
path: "/home",
component: Home,
exact: true
},
{
path: "/home/detail",
component: NoticeDetail,
exact: true
},
{
path: "/home/card",
component: CardListDetail,
exact: true
},
{
path: "/news",
component: NewScheduleList,
exact: true
},
{
path: "/type/:type", //重点关注这,umi处理动态路由
component: TypeList,
exact: true
}
];

而现在我们只需要在page文件夹下创建相应的文件就可以了

1
2
3
4
5
6
7
8
9
10
11
12
+ pages/
+ home/
- index.js # Home.js
+ detail/
- index.js # NoticeDetail.js
+ card/
- index.js # CardListDetail.js
+ news/
- index.js # NewScheduleList.js
+ type/
- $type.js # TypeList.js 文件名加$标识是个动态路由
- index.js # Index.js

其他具体的路由约定规则,可以参考umi 官网

umi 和 dva 架构对比

dva 项目之前通常都是这种扁平的组织方式,

1
2
3
4
5
6
7
8
9
10
11
+ models
- global.js
- a1.js
- a2.js
- b.js
+ services
- a.js
- b.js
+ routes
- PageA.js
- PageB.js

用了 umi 后,可以按页面维度进行组织,

1
2
3
4
5
6
7
8
9
10
11
12
13
+ models/global.js
+ pages
+ a
- index.js
+ models
- a1.js
- a2.js
+ services
- a.js
+ b
- index.js
- model.js
- service.js

好处是更加结构更加清晰了,减少耦合,一删全删,方便 copy 和共享。

参考链接

dva 官网
umi 配置
umi 和 dva 更配