dva是什么?
-
dva是一个基于Redux与React-saga的数据流解决方案。
-
为了简化开发流程,另外内置React-Router与Redux。
如何使用 ?
-
下载dva-cli
// 第一步
npm install dva-cli -g
dva -v // 查版本 确认安装成功
// 第二步
dva new your-project
cd your-project
npm run start
-
dva提供的API
import dva from 'dva'
const app = dva({
//指定给路由用的 history,默认是 hashHistory
history,
//初始化Model中state状态,比Model优先高, 默认 {}
initialState,
// 对应功能钩子函数
onError,
onAction,
onStateChange,
onReducer,
onEffect,
onHmr,
extraReducers,
extraEnhancers,
})
// 执行插件
app.use()
// 存储纯函数 修改数据的方法对象
app.model( required('..model/example.js').defaut )
// 路由配置文件,可对路由表以JavaScript对象的形式去配置, 如不要路由可直接返回组件,
app.router( requried('../router').default)
// 挂载启动
app.start('element');
-
Model是一个JavaScript对象, 它包含如下
export default {
// 每个Model 唯一的Key, model 外提交action 需添加 key 例如 example/add
namespace: 'example',
// 初始化状态
state: 0,
// 监听
subscriptions: {
// 函数名可以随便起
keyEvent({dispatch}){
window.onresize = function(){
dispatch({type: 'add'})
}
}
},
// 纯函树更新状态,触发视图更新
reducers: {
add({example}, action){
return {
...example,
count: example.count+1
}
},
reduce({example}, action){
return {
...example,
count: example.count+1
}
}
},
// 不是纯函数,如提交的异步action 走这里
effects: {
*asyncName({ars}, {call, put, select}){
// 执行异步操作;
yield call()
// Get state
yield select(({example}))
// 提交action
yield put({type: 'add'})
}
}
}
-
React Component 如何提交Action
import React from 'react';
import { connect } from 'dva'
class Example extends React.Component {
render(){
let {count, dispatch } = this.props;
return (
{count}
)
}
}
// 组件与dva model 绑定
const mapStateToprops = ({example}) =>{
return {...example, count: example.count}
};
export default connect(mapStateToprops)(Example);
Ant-Design-Pro 与 dva 结合使用
-
Router篇
// 通过JavaScript 对配置路由组件信息
const routerConfig = {
// hideInBreadcrumb: true,
// name: '工作台',
// authority: 'admin',
'/': {
component: dynamicWrapper(app, ['user', 'login'], () => import('../layouts/BasicLayout')),
},
'/dashboard/analysis': {
component: dynamicWrapper(app, ['chart'], () => import('../routes/Dashboard/Analysis')),
},
'/dashboard/monitor': {
component: dynamicWrapper(app, ['monitor'], () => import('../routes/Dashboard/Monitor')),
},
'/dashboard/workplace': {
component: dynamicWrapper(app, ['project', 'activities', 'chart'], () =>
import('../routes/Dashboard/Workplace')
),
}
};
-
Model篇 effects 与 reducers 的应用
// routes/user/Login.js 登录
handleSubmit = (err, values) => {
const { type } = this.state;
const { dispatch } = this.props;
if (!err) {
dispatch({
// model外提交action需要加model key 例如下方
type: 'login/login',
payload: {
...values,
type,
},
});
}
};
// layouts/BasicLayout.js 退出
handleMenuClick = ({ key }) => {
const { dispatch } = this.props;
if (key === 'triggerError') {
dispatch(routerRedux.push('/exception/trigger'));
return;
}
if (key === 'logout') {
dispatch({
type: 'login/logout',
});
}
};
// models/login.js
export default {
namescape: 'login',
status: undefined,
effects: {
// 登录
*login({ payload }, { call, put }) {
// fakeAccountLogin 异步函数
const response = yield call(fakeAccountLogin, payload);
yield put({
type: 'changeLoginStatus',
payload: response,
});
// .......
},
// 退出
*logout(_, { put }) {
yield put({
type: 'changeLoginStatus',
payload: {
status: false,
currentAuthority: 'guest',
},
});
reloadAuthorized();
// 切换路由到 登录页
yield put(
routerRedux.push({
pathname: '/user/login',
search: stringify({
redirect: window.location.href,
}),
})
);
},
},
reducers: {
// 更改状态,触发视图更新
changeLoginStatus(state, { payload }) {
setAuthority(payload.currentAuthority);
return {
...state,
status: payload.status,
type: payload.type,
};
},
},
}
-
Model篇 subscriptions 的应用
// 监听路由的改变
subscriptions: {
setup({ history }) {
// Subscribe history(url) change, trigger `load` action if pathname is `/`
return history.listen(({ pathname, search }) => {
if (typeof window.ga !== 'undefined') {
window.ga('send', 'pageview', pathname + search);
}
});
},
keyEvent({ diapatch }) {
// 监听窗口变化 去改变状态, 监听 表单输入等等。
window.onresize = function(){
diapatch({type: 'xx'})
}
},
},
Model篇 effects 的应用
-
call 与 apply 执行异步操作
// 执行异步操作 function* asyncFn(){ yield call(fn, args) yield apply(fn, [args]) // 如果要更改fn的this yield call([this,fn], args) yield apply([this,fn], [args]) }
-
takeEvery 与t akeLatest <作用:监听action>
// 如事件提交了多个action, 都会保留 function* asyncFn(){ // 如事件提交了多个action, 都会保留 yield takeEvery('监听的aciton', fn) // 如事件提交了多个action 只保留最后执行 那个 yield takeLatest('监听action', fn) }
-
put <提交action, 通过reducers 改变状态, 渲染视图>
function* asyncFn(){
yield put({type: 'xxxx', args })
}
-
select 获取module中的state
function* asyncFn(){
yield select()
}
-
put 并发请求应用场景使用,effects结合axios.all()
function getUserAccount() {
return axios.get('/toutiao/index?type=top&key=d2340952fcc9f969b9b920dca141f4fc');
}
function getUserPermissions() {
return axios.get('/wepiao/query?key=f0c96cee32a57753af183345cded5716');
}
function httpAsync(){
return axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (a,b) {
let params = {a,b}
return new Promise((reslove, reject)=> reslove(params));
}));
}
export function* incrementAsync() {
let data = yield call([obj, httpAsync])
yield put({ type: 'INCREMENT' })
}
// Our watcher Saga: 在每个 INCREMENT_ASYNC action spawn 一个新的 incrementAsync 任务
export function* watchIncrementAsync() {
yield takeEvery('INCREMENT_ASYNC', incrementAsync)
}
总结
- Redux 相比 dva, 用dva 文件关联数减少, 节约了文件管理成本,切换成本,代码更简洁。
- dva 集成了 Redux ,React/Router, React/saga ,dva/fetch, 不要在下载这些包了
- router 除了可以组件的形式写, 也可通过 JavaScript 对象嵌套配置路由信息