引言
最近工作需要用dva框架,同事幫我培訓了一下,有一點點認識,在此總結。
當然,以后對dva可能會了解更透徹,文章會不斷更新的。
初識
開始看架構代碼,沒有看文檔的時候,不知道里面的幾個關鍵字是什么意思:
- Effect
- Reducer
- Dispatch
- mapStateToProps
- NAMESPACE
一頭霧水,感覺好復雜。聽完同事講解以后,覺得redux代碼量好大。
雖然公司其他項目用的不是這個框架,但是最近項目需要,沒辦法,學唄!
關於dva
dva 是基於現有應用架構 (redux + react-router + redux-saga 等)的一層輕量封裝;
數據流向: 數據的改變發生通常是通過:
用戶交互行為(用戶點擊按鈕等)
瀏覽器行為(如路由跳轉等)觸發的
當此類行為會改變數據的時候可以通過 dispatch 發起一個 action ,如果是同步行為會直接通過Reducers 改變 State ,如果是異步行為(副作用)會先觸發 Effects 然后流向 Reducers 最終改變State 。 所以在 dva 中,數據流向非常清晰簡明,看最后思維導圖。
關於Redux-saga
Redus-saga 是一個 redux 的中間件,主要用來簡便而優雅的處理 redux 應用里的副作用(side effect相對於pure function這類概念而言的)。它之所以可以做到這一點主要是使用了ES6 里的一個語法: Generator 。使用 Generator 可以像寫同步的代碼一樣編寫異步代碼,這樣更加容易測試。(其實這里也不是特別懂,后面還要研究一下)
現在回頭看(雖然才過去兩天),感覺走了彎路。因為項目時間比較緊,同事跟我講完,我想都沒想,就按照他跟我說的邏輯,復制粘貼(在原來代碼基礎上),然后哪里報錯修哪里。
后來越弄越搞不清楚。所以,拿到新東西,開始一定是看文檔,看文檔,看文檔。
后來我看文檔,把不懂的再確認一遍,看一下之前別人是怎么寫的,然后從頭開始寫自己的任務:
1 添加路由(page)
2 添加布局以及組件
3 設計Model
4 connect關聯
上手
下面根據以上四個步驟,我們來看一個用例
這個就是同事跟我講解的時候寫的,很清晰明了
step1:routes
這里沒有寫組件,如果有組件這里可以抽出來。
關於多層級組件傳數據,我目前還不是完全明白,等后面搞明白了加上。
import React, { Component } from 'react'
import { connect } from 'dva'
import './index.less'
import { NAMESPACE } from '../../models/evaluateManage/constants';
import { Row , Col, Input, Button, Select } from 'antd'
const Option = Select.Option
const mapStateToProps = (state) => ({
...state[NAMESPACE],
description: state[NAMESPACE].description,
menuList: state[NAMESPACE].menuList,
})
const mapDispatchToProps = (dispatch) => ({
dispatch,
updateState(val, cb){
dispatch({
type:`${NAMESPACE}/updateState`,
payload:{
params: val,
cb: cb
}
})
},
getMenu(val, cb){
dispatch({
type: `${NAMESPACE}/getMenu`,
payload: {
params: val,
cb: cb
}
})
}
})
@connect(mapStateToProps, mapDispatchToProps)
class EvaluateManage extends Component{
constructor(props){
super(props)
this.state = {
inputMsg: undefined
}
}
syncMethod = () => {
const { inputMsg } = this.state
this.props.updateState({
description: inputMsg
}, (res) => {
debugger
})
}
asyncMethod = () => {
this.props.getMenu({
tenantId: "_1RNSq2S"
})
}
asyncMethod = () => {
this.props.getMenu({
tenantId: "_1RNSq2S"
})
}
asyncMethodCallback = () => {
this.props.getMenu({
tenantId: "_1RNSq2S"
},(res = []) => {
this.props.updateState({
menuList: res,
description: "通過回調機制"
})
})
}
inputMethod = (e) => {
let value = e.target.value
if(!value){
value = "我是描述"
}
this.setState({
inputMsg: value
})
}
render(){
const { inputMsg } = this.state
const { menuList = [] } = this.props
return <div styleName="rule-wrapper">
<h1 styleName="ruleTitle">{this.props.description}</h1>
<h1 styleName="ruleTitle">我是輸入內容:{inputMsg}</h1>
<Row>
<Col span={24}>
<Input onChange={this.inputMethod} value={inputMsg}/>
</Col>
</Row>
<Row>
<Col span={8}>
<Button size="large" onClick={this.syncMethod}>我是同步按扭</Button>
</Col>
<Col span={8}>
<Button size="large" onClick={this.asyncMethod}>我是異步按扭</Button>
</Col>
<Col span={8}>
<Button size="large" onClick={this.asyncMethodCallback}>我是異步按扭回調機制</Button>
</Col>
</Row>
<ul>
{
menuList.map((item) => {
const { name, code} = item
return <li>{name} {code}</li>
})
}
</ul>
</div>
}
}
export default EvaluateManage
inistate
const evaluateManage = {
"description": "我是描述",
"menuList": []
}
model
import * as constants from './constants'
import {initState} from "../initState";
import {apiCallTemplate} from 'Apis/api'
export const getMenu = apiCallTemplate('getMenu', constants.NAMESPACE)
export default {
namespace: constants.NAMESPACE,
state: initState[constants.NAMESPACE],
reducers: {
updateState(state, { payload }){
const { params, cb } = payload
cb && cb()
return {
...state,
...params
}
}
},
effects: {
*getMenu({ payload: {params = {},cb } }, { call, put}){
const { data } = yield call(getMenu, params)
const { menuList = [] } = data
if(cb){
cb(menuList)
}else{
yield put({
type: 'updateState',
payload: {
params: {
menuList,
description: "通過非回調機制"
}
}
})
}
}
},
subscriptions: {},
}
最終效果見下圖
點擊同步按鈕

點擊異步按鈕,這里調了getMenu接口

異步和異步回調效果差不多的,后者多了回調
dva思維導圖

寫在最后
model中的reducers跟effects是負責修改狀態state的方法,其中reducers是同步方法,effects是異步方法,reducers跟effects中的方法
組件中修改狀態需要通過在Action中調用dispatch方法來調用reduces或effects中的方法來實現,
因此需要在組件中定義Action方法,頁面根組件中的mapDispatchToProps里面負責定義Action,
所以在頁面根組件中需要在mapDispatchToProps中定義操作reducers或effects的方法(action)
dva將所有與數據操作相關的邏輯集中放在一個地方處理和維護,在數據跟業務狀態交互比較緊密的場景下,會使我們的代碼更加清晰可控。
對於一個大型管理系統,由於要進行大量的數據操作,在設計model時將不同類型的業務需求數據操作分開處理,便於維護。
項目的開發流程一般是從設計 model state 開始進行抽象數據,完成 component 后,將組件和model 建立關聯,通過 dispatch 一個 action ,在 reducer 中更新數據完成數據同步處理;
當需要從服務器獲取數據時,通過 Effects 數據異步處理,然后調用 Reducer 更新全局 state 。
以上一個單向的數據流動過程。
解決了 redux 中代碼分散和重寫問題。
總之,Dva:Build redux application easier and better。
這里代碼量確實比mobx的大,但這套框架也確實有可取之處。
阿里雲主要用的語言是React,架構: Redux,框架:dva
備注:
react高階組件學習
前端react迭代方向,盡量少用高階組件,后期不好維護。多將組件細分,組件越小越好,類似樂高積木一樣。
不過高階組件還是要會運用。
