来源:https://pengtikui.cn/dva.js-get-started/css
——------------------------------------------------------------------------------------前端
dva.js 是一个基于 redux、redux-saga 和 react-router 的轻量级前端框架。node
本文写的有点凌乱…react
安装 dva-cli 用于初始化项目:webpack
1
2
3
|
npm install -g dva-cli
# 或
yarn global add dva-cli
|
建立项目目录,并进入该目录:git
1
2
|
mkdir your-project
cd your-project
|
初始化项目:github
1
|
dva init
|
而后运行 npm start
或 yarn start
便可运行项目。web
项目初始化之后,默认的目录结构以下:npm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
|- mock
|- node_modules
|- package.json
|- public
|- src
|- asserts
|- components
|- models
|- routes
|- services
|- utils
|- router.js
|- index.js
|- index.css
|- .editorconfig
|- .eslintrc
|- .gitignore
|- .roadhogrc.mock.js
|- .webpackrc
|
先安装 antd
和 babel-plugin-import
:json
1
2
3
|
npm install antd babel-plugin-import --save
# 或
yarn add antd babel-plugin-import
|
babel-plugin-import
也能够经过 -D
参数安装到 devDependencies
中,它用于实现按需加载。
而后在 .webpackrc
中添加以下配置:
1
2
3
4
5
6
7
8
9
|
{
"extraBabelPlugins": [
[
"import", {
"libraryName": "antd",
"libraryDirectory": "es",
"style": true
}]
]
}
|
如今就能够按需引入 antd 的组件了,如 import { Button } from 'antd'
,Button 组件的样式文件也会自动帮你引入。
更多 .webpackrc
的配置请参考 roadhog 配置。
能够在 .webpackrc
中添加 theme
字段直接进行主题自定义,可是若是自定义的变量太多,建议将其单独提取取来方便管理。
建议在 ./src
目录下新建名为 theme.js
的文件,而后在 .webpackrc
中引入,以下:
1
2
3
|
{
"theme": "./src/theme.js"
}
|
theme.js
示例以下:
1
2
3
|
export default {
"primary-color": "#000",
}
|
更多可自定义的 antd 变量请参考 default.less。
使用 dva-cli 初始化的项目默认已经启用了 CSS Modules,若是不想使用 CSS Modules,在 .webpackrc
中添加如下配置项便可禁用:
1
2
3
|
{
"disableCSSModules": true
}
|
如需开发过程当中代理 API 接口,在 .webpackrc
中添加以下配置便可:
1
2
3
4
5
6
7
8
|
{
"proxy": {
"/api": {
"target": "http://your-api-server",
"changeOrigin": true
}
}
}
|
如需 mock 功能,在 .roadhogrc.mock.js
中添加配置便可,如:
1
2
3
|
export default {
'GET /api/users': { users: [{ username: 'admin' }] },
}
|
如上配置,当请求 /api/users
时会返回 JSON 格式的数据。
同时也支持自定义函数,以下:
1
2
3
|
export default {
'POST /api/users': (req, res) => { res.end('OK'); },
}
|
具体的 API 请参考 Express.js@4。
当 mock 数据太多时,能够拆分后放到 ./mock
文件夹中,而后在 .roadhogrc.mock.js
中引入。
HMR,即模块热替换,在修改代码后不须要刷新整个页面,方便开发时的调试。能够在 .webpackrc
中添加以下配置以使用 HMR:
1
2
3
4
5
6
7
8
9
|
{
"env": {
"development": {
"extraBabelPlugins": [
"dva-hmr"
]
}
}
}
|
若是无效,请尝试更新一下 babel-plugin-dva-hmr
。
env
字段是针对特定环境进行配置,由于 HMR 只在开发环境下使用,因此将配置添加到 development
字段便可,运行 npm run build
时的环境变量为 production
。
dva 内置了 dynamic
方法用于实现组件的动态加载,用法以下:
1
2
3
4
5
6
7
8
9
|
import dynamic from 'dva/dynamic';
const UserPageComponent = dynamic({
app,
models:
() => [
import('./models/users'),
],
component:
() => import('./routes/UserPage'),
});
|
实际使用时,能够对其进行简单的封装,不然每一个路由组件都这么写一遍很麻烦。
dva-loading 是一个用于处理 loading 状态的 dva 插件,基于 dva 的管理 effects 执行的 hook 实现,它会在 state 中添加一个 loading
字段(该字段可自定义),自动帮你处理网络请求的状态,不须要本身再去写 showLoading
和 hideLoading
方法。
在 ./src/index.js
中引入使用便可:
1
2
3
|
import createLoading from 'dva-loading';
const app = dva();
app.use(createLoading(opts));
|
opts
仅有一个 namespace
字段,默认为 loading
。
Model 是 dva 最重要的部分,能够理解为 redux、react-redux、redux-saga 的封装。
一般项目中一个模块对应一个 model,一个基本的 model 以下:
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
|
import { fetchUsers } from '../services/user';
export default {
namespace:
'user',
state: {
list: [],
},
reducers: {
save(state, action) {
return {
...state,
list: action.data,
};
},
},
effects: {
*fetch(action, { put, call }) {
const users = yield put(fetchUsers, action.data);
yield put({ type: 'save', data: users });
},
},
subscriptions: {
setup({ dispatch, history }) {
return history.listen(({ pathname }) => {
if (pathname === '/user') {
dispatch({
type: 'fetch' });
}
});
},
},
}
|
namespace
是该 model 的命名空间,同时也是全局 state
上的一个属性,只能是字符串,不支持使用 .
建立多层命名空间。
state
是状态的初始值。
reducer
相似于 redux 中的 reducer,它是一个纯函数,用于处理同步操做,是惟一能够修改 state
的地方,由 action
触发,它有 state
和 action
两个参数。
effects
用于处理异步操做,不能直接修改 state
,由 action
触发,也可触发 action
。它只能是 generator
函数,而且有 action
和 effects
两个参数。第二个参数 effects
包含 put
、call
和 select
三个字段,put
用于触发 action
,call
用于调用异步处理逻辑,select
用于从 state
中获取数据。
subscriptions
用于订阅某些数据源,并根据状况 dispatch 某些 action,格式为 ({ dispatch, history }, done) => unlistenFunction
。
如上的一个 model,监听路由变化,当进入 /user
页面时,执行 effects
中的 fetch
,以从服务端获取用户列表,而后 fetch
中触发 reducers
中的 save
将从服务端获取到的数据保存到 state
中。
注意,在 model 中触发这个 model 中的 action
时不须要写命名空间,好比在 fetch
中触发 save
时是 { type: 'save' }
。而在组件中触发 action
时就须要带上命名空间了,好比在某个组件中触发 fetch
时,应该是 { type: 'user/fetch' }
。
在 ./src/index.js
中能够看到以下代码:
1
2
|
import dva from 'dva';
const app = dva();
|
app 就是 dva 实例,建立实例时 dva 方法能够传入一些参数,以下:
history 是给路由用的,默认为 hashHistory
,若是想要使用 browserHistory,须要安装 history
,而后在 ./src/index.js
引入使用:
1
2
3
4
5
|
import dva from 'dva';
import createHistory from 'history/createBrowserHistory';
const app = dva({
history: createHistory(),
});
|
initialState 是 state 的初始数据,优先级高于 model 中的 state,默认为 {}
。
其余以 on
开头的均为钩子函数,更多请参考 dva API。
当写完 model 和组件后,须要将 model 和组件链接起来。dva 提供了 connect
方法,其实它就是 react-redux
的 connect
。用法以下:
1
2
3
4
5
6
7
8
9
10
11
12
|
import React from 'react';
import { connect } from 'dva';
const User = ({ dispatch, user }) => {
return (
<div></div>
)
}
export default connect(({ user }) => {
return user;
})(User);
|
connect 后的组件除了能够获取到 dispatch
和 state
,还能够获取到 location
和 history
。
effects
和 subscriptions
抛出的错误都会通过 onError
钩子函数,因此能够在 onError
中进行全局错误处理。
1
2
3
4
5
|
const app = dva({
onError(err, dispatch) {
console.error(err);
},
});
|
若是须要对某些 effects
进行特殊的错误处理,能够使用 try catch
。
dva 集成了 isomorphic-fetch
用于处理异步请求,而且使用 dva-cli 初始化的项目中,已经在 ./src/utils/request.js
中对 fetch 进行了简单的封装,能够在这里根据服务端 API 的数据结构进行统一的错误处理。
固然若是你不想用 fetch,彻底能够引入本身喜欢的第三方库,没有任何影响,打包时也不会将 isomorphic-fetch
打包进去。