引言
最近工作需要用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迭代方向,盡量少用高階組件,后期不好維護。多將組件細分,組件越小越好,類似樂高積木一樣。
不過高階組件還是要會運用。