chaiguanpeng.github.io/
回顧
- 初識react(一) 揭開jsx語法和虛擬DOM面紗
- 初識react(二) 實現一個簡版的html+redux.js的demo
- 初識react(三)在 react中使用redux來實現簡版計數器
- 初識react(四) react中非同步解決方案之 redux-saga
- 初識react(五) 資料流終極解決方案 dva(零配置)
糾正下,零配置是由umi幫我們實現的,不是dva幫我們做的,但是dva-cli在下一個版本(即dva-cli 1.0)已經內建了umi,簡直開發者福音,有興趣朋友可以體驗下最新版本。
npm i dva-cli@next -g //安裝下一代dva-cli,內建umi
dva new dvaTest
cd dvaTest
npm start
複製程式碼
我們這裡還是從最基本的dva開始講起,瞭解流程。重點:dva並沒有發明新的概念,全都是以前提到的。只是進行了一層封裝,對redux、saga中的概念很清楚的話,dva就是白給你的,沒有難點,不會來找我。
簡介
- 基於 redux、redux-saga 和 react-router 的輕量級前端框架。
- dva是基於react+redux最佳實踐上實現的封裝方案,簡化了redux和redux-saga使用上的諸多繁瑣操作
資料流向
- 資料的改變發生通常是通過:
- 使用者互動行為(使用者點選按鈕等)
- 瀏覽器行為(如路由跳轉等)觸發的
- 當此類行為會改變資料的時候可以通過 dispatch 發起一個 action,如果是同步行為會直接通過 Reducers 改變 State ,如果是非同步行為(副作用)會先觸發 Effects 然後流向 Reducers 最終改變 State。
實現的demo效果
由於dva比較簡單,沒有什麼新概念用例子講解會更明白。最後要實現一個非同步獲取資料 num,然後點選計數器 + num的效果
- 目錄結構
1、主入口檔案
import React from 'react';
import dva from 'dva';
import Counter from './Counter';
//dva是一個函式,通過執行它可以拿到一個app物件
let app = dva();
//一個模板就是一個狀態,然後把reducer和狀態 寫在一起了,
//新增一個模組
app.model({
xxxx
});
//引數是一個函式,此應用本身就是要渲染函式的返回值
app.router(() => <Counter />);
//本質是啟動應用,就是通過app.router獲取元件,並且通過ReactDOM渲染到容器內容
app.start('#root');
複製程式碼
以上是最基本的dva的主入口檔案,簡單的3個API,app.model、app.router、app.start,就已經講react、redux-router、redux、redux-saga整合一起,簡直開發者福音。我們講解下怎麼用
- dva是一個函式,通過執行它可以拿到一個app物件
- app.model()新增一個模組,下面重點講解
- app.router()接受函式,然後渲染函式返回值
- app.start('#root'),通過app.router獲取元件,然後通過ReactDom渲染到容器
1.1、app.model()用法
- 接受一個物件,把state、reducers、effects全部寫在這,便於維護。
app.model({
//名稱空間。因為一個應用會有很多個模型,每個模型要有一個名字
namespace: 'counter',
//此名稱空間的預設狀態
state: { current: 0, highest: 0 },
//它是用來接收action,修改倉庫狀態的
reducers: {
save(state, action) {
return { current:state.current+action.payload };
}
}
});
複製程式碼
看見這些名詞應該很熟悉吧
- namespace名稱空間,我們需要給模型一個名字
- state=>狀態,就是redux中的狀態
- reducers=>處理器,就是redux中的處理器
在強調遍,dva沒有發明新的概念,只是進行了一層封裝。讓狀態更利於維護
2、編寫Counter.js元件
import React from 'react';
import { connect } from 'dva';
class Counter extends React.Component {
render() {
return (
<div className="container">
<div className="current">
當前記錄:{this.props.current}
</div>
<div className="addButton">
<button onClick={() => this.props.dispatch({ type: 'counter/save',payload:2 })}>+</button>
</div>
</div>
)
}
}
export default connect(
state => {
return state.counter;
}
)(Counter);
複製程式碼
不過多解釋,有2個地方需要注意:
- 元件內部派發動作時,type:'counter/add',前面多了counter(名稱空間)
- connect時的狀態是總的狀態,需要制定下需要counter的狀態
目前為止,dva流程已經跑通了,是不是很簡單,我們測試下是否能點選加2
完美實現,說好的非同步呢,接下來我們用express編寫一個簡單介面
3、編寫服務端介面 server.js
我們用express編寫簡單介面,不講解express用法。 express直通車
let express = require('express');
let cors = require('cors'); //解決跨域的包
let app = express();
app.use(cors()); //使用中介軟體cors
app.get('/amount', function (req, res) {
res.send({ num: 5 });
});
app.listen(3000);
複製程式碼
- 接下來啟動服務,看下效果
4、客戶端發請求獲取資料
由於案例比較簡單,都寫在了src/index.js中
function getAmount() {
return fetch('http://localhost:3000/amount', {
headers: {
"Accept": "application/json"
}
}).then(res => res.json());
}
複製程式碼
5、在app.model中新增effects(副作用) (就是redux-saga中的effects)
effects: {
//表示這是一個generator effect=redux-saga/effects
*add(action, { call, put }) {
let { num } = yield call(getAmount);
yield put({ type: 'save', payload: num });
}
},
複製程式碼
先非同步獲取資料,然後再派發動作修改狀態,接著重新整理檢視
6、對應的元件新增一個非同步記數的按鈕
<button onClick={() => this.props.dispatch({ type: 'counter/save',payload:2 })}>同步加2</button>
<button onClick={() => this.props.dispatch({ type: 'counter/add' })}>非同步記數</button>
複製程式碼
- 增加了一個非同步計數按鈕,會派發add動作型別。
- add型別被effects(副作用)中的add監聽到,執行 getAmount()非同步獲取資料
- 拿到資料後派發save動作,被reducers處理
- 頁面重新整理
測試結果
完結
dva 簡化了redux和redux-saga使用上的諸多繁瑣操作,便於我們開發,可維護性也更高,配合umi使用,號稱零配置,下篇文章會講解dva+umi使用
- 如果對您有幫助,點個喜歡再走唄
- 更多優質文章參考
- redux所有原始碼解析戳這裡