dva.js 简介
- dva 是阿里前端架构师 sorrycc 带 team 研发的一套轻量级前端框架,其目的是尽量避免前端重复性劳动,简化开发流程。
一个完整的 dva 脚手架应该包含以下内容:- 自动创建一个包含 package.json 的项目。
- 自动创建成体系的目录结构。
- 自动安装项目需要的基础包。
- 集成代码检查工具 ESLint。
- 集成模拟接口工具 Mock。
- 集成服务启动打包工具 Roadhog。
- 集成版本控制工具 Git。
初始化
- 安装
dva-cli
用于初始化项目:npm install dva-cli -g
- 创建项目目录,并进入该目录:
mkdir your-project cd your-project
- 初始化项目:
dva init
- 运行
npm start
运行徐项目。
目录结构
- 目录初始化以后,目录默认如下:
|- 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
- mock 用于存放 mock 数据的文件
- public 一般用于存放静态文件,打包时会被直接复制到输出目录(./dist)
- src 文件夹用于存放项目源代码
- asserts 用于存放静态资源,打包时会经过 webpack 处理;
- components 用于存放 React 组件,一般是该项目公用的无状态组件;
- models 用于存放模型文件;
- routes 用于存放需要 connect model 的路由组件;
- services 用于存放服务文件,一般是网路请求等;
- utils 工具类库;
- routers.js 路由文件;
- index.js 项目的入口文件;
- index.css 一般是公用样式
- .editorconfig 编辑器配置文件
- .eslintrc ESLint配置文件
- .gitignore Git忽略文件
- .roadhogrc.mock.js Mock配置文件
- .webpackrc 自定义的webpack配置文件,JSON格式,如果需要JS格式,可修改为.webpackrc.js
antd按需引入
- 先安装
antd
和babel-plugin-import
:npm install antd balbel-plugin-import --save
babel-plugin-import
也可以通过-D
参数安装到devDependencies
中,它用于实现按需加载。
之后在.webpackrc
中添加如下配置:
现在就可以按需引入{ "extraBabelPlugins": [ ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": true }] ] }
antd
的组件了,如:import { Button } from 'amtd';
,Button组件的样式文件也会自动引入。
更多.webpackrc
配置请参考 roadhog配置
自定义 antd 主题
-
可以在
.webpackrc
中添加theme
字段直接进行主题自定义,但是如果自定义的变量太多,建议单独提取出来,方便管理。建议在
./src
目录下新建名为theme.js
的文件,然后再.webpackrc
中引入,如下:{ "theme": "./src/theme.js" }
theme.js
文件如下:export default { "primary-color": "#000", }
更多可自定义的 antd 变量请参考 default.less
CSS Modules
- 使用 dva-cli 初始化的项目默认已经启用了 CSS Modules,如果不想使用CSS Modules,在
.webpackrc
中添加一下配置即可禁用:"disableCSSModules": true
开发代理
- 开发过程中如果需要代理API接口,在
webpackrc
中添加如下配置:{ "proxy": { "/api": { "target": "http://your-api-server", "changeOrigin": 'true } } }
Mock
- 入需 Mock 功能, 在
.webpackrc.mock.js
中配置即可,如:
如上配置,当请求export default { 'GET /api/users': { users: [{ username: 'admin' }] }, }
/api/users
时会返回 JSON 格式的数据。
同时也支持自定义函数,如下:
具体的 API 请参考 Express.js@4.export default { 'POST /api/users': (req, res) => { res.end('OK'); }, }
当 mock 数据太多是,可以拆分放到./mock
文件夹中,然后在.roadhogrc.mlck.js
中引入。
HMR
- HMR,即模块热替换,在修改代码后不需要刷新整个页面,方便开发时的调试。可以在
.webpackrc
中添加如下配置来使用HMR:
如果无效,请尝试更新一下{ "env": { "development": { "extraBabelPlugins": [ "dva-hmr" ] } } }
bebel-plugin-dva-hmr
。
env
字段是针对特定环境进行配置,因为 HMR 只在开发环境下使用,所以将配置添加到development
字段即可,运行npm run build
时的环境变量为production
。
组件动态加载
- dva内置了
dynamic
方法用于实现组件的动态加载,用法如下:
实际使用的时候,可以对其进行简单的封装,否则每个路由组件都这么写一遍很麻烦。import dynamic from 'dva/dynamic'; const UserPageComponent = dynamic({ app, models: () => [ import('./models/users'), ], component: () => import('./routes/UserPage'), });
dva-loading
- dva-loading 是一个用于处理 loading 状态的 dva 插件,基于 dva 的管理 effects 执行的 hook 实现,它会在 state 中添加哟个
loading
字段(该字段可自定义),自动处理网络请求的状态,不需要自己再去写showLoading
和hideLoading
方法。
在./src/index.js
中引入使用即可:import createLoading from 'dva-loading'; const app = dva(); app.use(createLoading(opts));
opts
仅有一个namespace
字段,默认为loading
。
Model
-
Model 是 dva 最重要的部分,可以理解为 redux、react-redux、 redux-saga 的封装。
通常一个项目中一个模块对应一个 model ,一个基本的 model 如下:
import { fetchUsers } from '../services/user'; export default { namespace: 'user', stat: { list: [], } reducers: { save(state, action) { return { ...state, list: action.data }; }, }, effects: { * fetch(action, { put, call }) { const users = yield put(fetchUsers, action.sata); 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 是不需要写命名空间,比如在 `fetch` 中触发 `save` 时是 `{ type: 'save' }`。而在组件中触发 `action` 时就需要带上命名空间了,比如在某个组件中触发 `fetch` 时,应该是 `{ type: 'user/fetch }`。
app
-
在
./src/index.js
中可以看到如下代码:import dva from 'dva'; const app = dva();
app 就是 dva 的实例,创建实例时 dva 方法传入一些参数,如下:
- history
- initialstate
- onError
- onAction
- onStateChange
- onReducer
- onEffects
- onHmr
- extraReducers
- extraEnhancers
history 是给路由器用的,默认为
hashHistory
,如果想要使用 browserHistory,需要安装history
,然后在。/src/index.js
引入使用:import dva from 'dva'; impoer createHistory from 'history/createBroeserHistory'; const app = dva({ history: createHistory(), });
initialState 是 state 的初始化数据,优先级高于 model 中的state,默认为
{}
。
其他以on
开头的均为钩子函数。
connect
- 当写完 moedl 和组件之后,需要将 model 和组件连接起来。 dva 提供了
connect
的方法,其实它就是react-redux
的connect
。用法如下:
connect 后的组件除了可以取到import React from 'react'; import { connect } from 'dva'; const User = ({ dispatch, user }) => { return ( <div></div> ) } export default connect(({ user }) => { return user; })(User);
dispatch
和state
,还可以获取到location
和history
。
错误处理
-
effects
和subscriptions
抛出的错误都会经过onError
钩子函数,所以可以在onError
中进行全局错误处理。
如果需要对某些const app = dva({ onError(err, dispatch) { console.log(err); }, });
effects
进行特殊的错误处理,可以使用try catch
。
异步请求
-
dva 集成了
isomorphic-fetch
用于处理异步请求,并且使用 dva-cli 初始化的项目中,已经在./src/utils/request.js
中对 fetch 进行了简单的封装,可以在这里根据服务端 API 的数据结构进行统一的错误处理。当然,如果不想使用 fetch ,完全可以引入自己喜欢的第三方库,没有任何影响,打包时也不会将
isomorphic-fetch
打包进去。
网友评论