dva 首先是一个基于 redux 和 redux-saga 的数据流方案,而后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,因此也能够理解为一个轻量级的应用框架。css
个人我的理解:dva的核心实际上是 saga的封装,将action,reducer等等所有引入到model中。vue
过多的废话也就再也不阐述了,欲知详情,请看官网,本文的目的就是快速开始,让一个拥有react+redux基础的人能够快速使用dva.jsreact
配置环境安装依赖之类的就很少说了,请看官方文档
git
dva new dva-quickstart
咱们获得初始项目,目录结构以下:
接下里将会逐个目录解释,请注意看注释github
import dva from 'dva'; //引入依赖 import './index.css'; // 1. Initialize const app = dva(); //初始化 dva应用 // 2. Plugins // app.use({}); //使用中间件 // 3. Model // app.model(require('./models/example').default); // 加载model层 (后面详细解释model) // 4. Router app.router(require('./router').default); // 引入router // 5. Start app.start('#root'); // 挂载dva应用
基本上就是这样,多余的没什么好说的
vue-router
import React from 'react'; import { Router, Route, Switch } from 'dva/router'; // 引入 router,用的就是 react-router import IndexPage from './routes/IndexPage'; // 引入路由绑定的高阶组件 // 按照从上到下的顺序开始匹配url规则,匹配到了就是展现对应的组件view function RouterConfig({ history }) { return ( <Router history={history}> <Switch> <Route path="/" exact component={IndexPage} /> </Switch> </Router> ); } export default RouterConfig;
在routes目录下,是路由页面,由多个高阶组件渲染而成,固然,刚初始化的项目天然没有写高阶组件,在后面的实战操做中,咱们将以 路由页面 => 高阶组件 => 基础组件 路由绑定model层,高阶组件绑定路由的action事件,基础组件绑定原生事件,在路由中触发action更新数据流
的逻辑 完成一个简单标准的dva过程redux
import React from 'react'; import { connect } from 'dva'; import styles from './IndexPage.css'; // 在这个方法中,咱们返回一个dom结构 // 而且 在圆括号中 能够接受一个大对象(包含不少东西),也能够解构 只取其中的state和dispatch,具体能够在后面看 function IndexPage() { return ( <div className={styles.normal}> <h1 className={styles.title}>Yay! Welcome to dva!</h1> <div className={styles.welcome} /> <ul className={styles.list}> <li>To get started, edit <code>src/index.js</code> and save to reload.</li> <li><a href="https://github.com/dvajs/dva-docs/blob/master/v1/en-us/getting-started.md">Getting Started</a></li> </ul> </div> ); } IndexPage.propTypes = { }; // 这里 connect方法就是redux的connect,后面的IndexPage表示绑定的高阶组件 // 在connect的第一个括号中,是能够拿到全部的model对象,这样就能够把对应的model对象绑定到咱们的高阶组件上 export default connect()(IndexPage);
看了上面的注释很蒙也不要紧,由于纸上谈兵,甚至,我兵都没有出来,你只须要知道,connect的做用及过程就行了api
export default { namespace: 'example', // 命名空间 做为 connect方法 中获取model对象state的 id state: {}, // 初始化state subscriptions: { // 订阅 setup({ dispatch, history }) { // eslint-disable-line }, }, effects: { // 异步action的handler *fetch({ payload }, { call, put }) { // eslint-disable-line yield put({ type: 'save' }); }, }, reducers: { //react-redux的reducers 用来接收action而且处理数据更新 save(state, action) { return { ...state, ...action.payload }; }, }, };
当咱们在高阶组件中经过connect绑定了高阶组件和model,而且在index.js中引入这个model,就可使用标准流程:在subscriptions方法中订阅路由变化,当路由与高阶组件相对应,调用effects请求数据,拿到数据reducer更新数据
服务器
代码就不贴了,你们应该都知道这里面作什么babel
这里封装了一些公共使用的方法
项目地址:https://github.com/zhaowanhua...
接下来,咱们将quick-start项目改形成一个按照dva标准流程的小项目(如上图),帮助你们理解和使用
首先咱们把上面那些文件夹下面的文件所有删干净
import dva from 'dva'; import './index.css'; import createHistory from 'history/createBrowserHistory'; // 这个方法里面 能够配置router的 路由模式,好比hash或者H5 histroy, // 具体区别能够参考个人文章 vue-router,单页应用原理一致的 const app = dva({ history: createHistory() }); // 2. Plugins // app.use({}); // 3. Model app.model(require('./models/List').default); // 引入model // 4. Router app.router(require('./router').default); // 5. Start app.start('#root');
import React from 'react'; const Item = ({ num, id, OnDelete }) => { return ( <li onClick={() => OnDelete(id)}> {num} </li> ); }; Item.propTypes = {}; export default Item;
export default { namespace: 'list', // 这个namespace 是model的惟一识别id,在connect中须要使用这个绑定 state: {}, subscriptions: { setup({ dispatch, history }) { // eslint-disable-line return history.listen(({ pathname }) => { if (pathname === '/') { dispatch({ type: 'fetch', payload: {} }); } }); }, }, effects: { * fetch({ payload }, { call, put }) { // eslint-disable-line // 这里伪装 获取到了服务器的数据 const fetchData = [0, 1, 2, 3] yield put({ type: 'save', list: fetchData }); }, }, reducers: { // 保存 save(state, action) { return {...state, list: action.list }; }, // 新增 add(state, action) { const [..._arr] = {...state }.list; _arr.push(_arr.length) return { ...state, list: _arr } }, // 删除 del(state, action) { return { ...state, list: state.list.filter((item, index) => { return index !== action.id }) } }, }, };
写好model 是要在index.js中引入的,否则没有效果
import React from 'react'; import Item from './Item' // 经过prop 把路由页面的action触发方法绑定过来,传递给子组件(OnDelete),也能够在当前组件触发,如OnAdd function List({ OnAdd, OnDelete, list }) { const List = list.map((num, index) => <Item num={num} key={index} id={index} OnDelete={OnDelete}></Item>); return ( <div> {List} <button onClick={OnAdd}>Add</button> </div> ); } List.propTypes = {}; export default List;
import React from 'react'; import { connect } from 'dva'; import List from '../components/List' //咱们在路由页面里面渲染高阶组件,写好action,经过prop传递给基础组件 // 这里引入的list 对应 model中的namespace function IndexPage({ dispatch, list }) { function handleAdd() { dispatch({ type: 'list/add' }); } function handleDelete(id) { dispatch({ type: 'list/del', id: id, }); } return ( <div> <List list={list} OnAdd={handleAdd} OnDelete={handleDelete}></List> </div> ); } IndexPage.propTypes = {}; // 经过connect方法绑定路由页面和model,你能够把connect方法的第一个参数(方法里的) 打印出来看看都有什么东西,不要让解构扰乱了你的眼睛,connect((obj)=>{console.log(obj)})() export default connect(({ list }) => { return list; // 这里是state中的list,经过connect,在每次数据更新的时候,流向咱们的view,更新视图,你能够在这里"打桩",看看具体的数据流动 })(IndexPage);
以上是我最近学习的想法和思考后获得的内容,但愿对你们有所帮助,写的比较随意,在内容中若是有问题或者想法不对,请予指正,也能够提出新的问题,咱们共同探究.