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);
以上是我最近學習的想法和思考後獲得的內容,但願對你們有所幫助,寫的比較隨意,在內容中若是有問題或者想法不對,請予指正,也能夠提出新的問題,咱們共同探究.