介绍
dva 是基于现有应用架构 (redux + react-router + redux-saga 等)的一层轻量封装,没有引入任何新概念,全部代码不到 100 行。
dva 是 framework,不是 library,类似 emberjs,会很明确地告诉你每个部件应该怎么写,这对于团队而言,会更可控。另外,除了 react 和 react-dom 是 peerDependencies 以外,dva 封装了所有其他依赖。
dva 实现上尽量不创建新语法,而是用依赖库本身的语法,比如 router 的定义还是用 react-router 的 JSX 语法的方式(dynamic config 是性能的考虑层面,之后会支持)。
他最核心的是提供了 app.model
方法,用于把 reducer, initialState, action, saga 封装到一起,比如:
app.model({ namespace: 'products', state: { list: [], loading: false, }, subscriptions: [ function(dispatch) { dispatch({type: 'products/query'}); }, ], effects: { ['products/query']: function*() { yield call(delay(800)); yield put({ type: 'products/query/success', payload: ['ant-tool', 'roof'], }); }, }, reducers: { ['products/query'](state) { return { ...state, loading: true, }; }, ['products/query/success'](state, { payload }) { return { ...state, loading: false, list: payload }; }, }, });
简单明了的Dva数据流向
数据的改变发生通常是通过用户交互行为或者浏览器行为(如路由跳转等)触发的,当此类行为会改变数据的时候可以通过 dispatch 发起一个 action,如果是同步行为会直接通过 Reducers 改变 State ,如果是异步行为(副作用)会先触发 Effects 然后流向 Reducers 最终改变 State
Dva Router控制
dva 实例提供了 router 方法来控制路由,使用的是react-router
const app = dva(); import { Router, Route } from 'dva/router'; app.router(({history}) => <Router history={history}> <Route path="/" component={HomePage} /> </Router> );
dva 应用的最简结构(带 model)
dva 提供多个 effect 函数内部的处理函数,比较常用的是 call 和 put。
call:执行异步函数
put:发出一个 Action,类似于 dispatch
// 创建应用 const app = dva(); // 注册 Model app.model({ namespace: 'count', state: 0, reducers: { add(state) { return state + 1 }, }, effects: { *addAfter1Second(action, { call, put }) { yield call(delay, 1000);//异步操作 yield put({ type: 'add' });//类似于dispatch发action }, }, }); // 注册视图 app.router(() => <ConnectedApp />); // 启动应用 app.start('#root');
AntDesignPro1.0项目中的Dva
1.index.js
const app = dva({ history: createHistory(),//history可以用来跳转路由内含location属性,这里修改history默认接口,其他接口不变----初始化 }); // 2. Plugins app.use(createLoading());//加载插件这里应该加载的是加载动画插件 // 3. Register global model app.model(require('./models/global').default);//将src/modles里面的东西灌进去,通过namespace取 // 4. Router app.router(require('./router').default);//全局挂载路由信息 // 5. Start app.start('#root'); export default app._store;
2.router.js
export const getRouterData = app => { const routerConfig = { '/': { component: dynamicWrapper(app, ['user', 'login'], () => import('../layouts/BasicLayout')), }, '/person/personbasetwo': {//添加路径指向引入的组件,这条数据会被getRoutes函数渲染成真正的<Route>包裹的路由 component: dynamicWrapper(app, ['personbaseTwo'], () => import('../routes/Person/PersonBaseTwo')), }, '/person/baseInfo/:id': {//dynamicWrapper函数会吧[]里面数据放到app的model属性里,app是dva的实例 component: dynamicWrapper(app, ['personbase'], () => import('../routes/Person/PersonBase/BaseInfo')), }, ·······
3.connect连接model
这里用解构赋值从model中取值,为组件导入props,loading为dva提供的动画插件*/ @connect(({ personbaseTwo, loading }) => ({ personbaseTwo, searchLoading: loading.effects['personbaseTwo/getList'], //loding被这个异步函数影响,异步操作中就为ture,结束就为false loading: loading.effects['personbaseTwo/listpage'], }))//从model中取数据生成自己想要的对象结构通过@修饰器放到下面组件中去 class personbaseTwo extends Component { constructor(props){ super(props); this.state = { } } componentWillMount(){//组件将要渲染时拿到默认的一页多少条和当前页这些数据 const { personbaseTwo:{pagination} }= this.props; const { page,pageSize } = pagination; this.props.dispatch({//转到namespace为personbaseTwo下面的listpage方法拿到页码为page的数据 type:'personbaseTwo/listpage',//接口根据page只去此页数据 payload:{ page, pageSize, }, }); } ·······
4.跳转路由
onOk() {//点击确定执行的函数 const {id}= record; than.props.dispatch(routerRedux.push({//用来跳转路由的 pathname: `/person/baseInfoTwo/${id}`,//用这个pathname重新渲染路由页面并传ID })) },
dva请求接口成功后的回调提示
dispatch({ type:'upImgModel/upImg', payload:{ from:'DEFAULT', name:values.name, base64:url.replace(/^data:image/w+;base64,/, ""), dimension:JSON.stringify({ width, heigh: height }), }, callback: (state, msg) => { if(state === 'success') { Toast.success(msg,2,null,false) const url = sessionStorage.getItem("returnUrl")?sessionStorage.getItem("returnUrl"):'/Signature' router.push(url) } else { Toast.fail(msg,2,null,false) } } })
当判断code成功的时候 执行回调
effects: { *upImg({ payload,callback }, { call, put }) { const response = yield call(postImg, payload); let reg = new RegExp(/0000$/, 'i') if(reg.test(response.code)) { yield put({ type: 'save', payload: response.data }); yield callback('success', '上传图片成功') }else{ let { message="图片上传失败"} = response yield callback('error',message) } } },
dvaJS实战
https://github.com/sorrycc/blog/issues/18
参考文档
https://dvajs.com/knowledgemap/#es6-%E5%AF%B9%E8%B1%A1%E5%92%8C%E6%95%B0%E7%BB%84