先說些廢話
最近在開發React技術堆疊的專案產品,對于資料狀態的管理使用了Dva.js,作為一個資深的ow玩家,我看到這個名字第一反應就是————這不是ow里的一個女英雄嗎?仔細閱讀了官方檔案之后,發現開發者還真是因為這個角色獲得靈感,來命名這個資料狀態管理插件,果然開發大佬都是作業和休閑兩不誤~
學過React的同學都知道它的技術堆疊非常多且雜,所以每當你使用React的時候都需要引入很多的模塊,那么Dva就是把這些用到的模塊集成在一起,比如一些需要引入的依賴react-saga/react-loger、必寫的ReactDOM.render、provider、connect包裹等都省去不寫,形成一定的架構規范,大大提高我們的開發效率
今天,就來寫一份檔案,幫助后續使用Dva的開發者更好得在實際專案中(PS:需要是以UMI為基礎框架,純Dva來構建專案可以直接看文章結尾的參考檔案串列)上手使用
什么是Dva
Dva首先是一個基于redux和redux-saga的資料流方案,然后為了簡化開發體驗,Dva還額外內置了react-router和fetch,所以也可以理解為一個輕量級的應用框架,
在我目前的專案中,更多是使用資料狀態管理的功能,他在我司的fish框架中做了內嵌,在主流的React開發框架UMI中也做了內嵌適配,使用起來非常方便快速,
Dva設計的目的就是簡化元素,降低難度,讓你不用管他怎么實作的,我們按照默認的這個規則去寫就可以
資料流向
資料的改變發生通常是通過用戶互動行為或者瀏覽器行為(如路由跳轉等)觸發的,當此類行為會改變資料的時候可以通過dispatch發起一個action,如果是同步行為會直接通過reducers改變states,如果是異步行為(副作用)會先觸發effects然后流向reducers最終改變states

分層開發
無論是Vue還是React開發,實際的大型應用一定有嚴格的分層開發規范,確保后續開發的可維護性,主要的分層結構有以下幾點:
- Page 負責與用戶直接打交道:渲染頁面,接受用戶的操作輸入,側重于展示型互動性邏輯,這里需要了解無狀態組件
- Model 負責處理業務邏輯,為 Page 做資料、狀態的讀寫、變換、暫存等,
Dva中model就是做了這一層的操作 - Service 負責與 HTTP 介面對接,進行純粹的資料讀寫
基礎概念
- namespace
model的命名空間,同時也是他在全域state上的屬性- 只能用字串,不支持通過
.的方式創建多層命名空間,相當于這個model的key - 在組件里面,通過
connect這個key將想要引入的model加入
import { connect } from 'dva'; export default connect(({ namespaceValue }) => ({ ...namespaceValue }))(DvaCompoent); - state
- 表示
model的狀態資料 - 操作的時候每次都要當作不可變資料
immutable data來對待,保證每次都是全新物件,沒有參考關系
- 表示
- reducer
- 必須是純函式,有固定輸入輸出,主要目的是修改自身
state - 接受兩個引數:之前已經累積運算的結果和當前要被累積的值,回傳的是一個新的累積結果,該函式把一個集合歸并成一個單值
- 需要注意的是同樣的輸入必然得到同樣的輸出,它們不應該產生任何副作用
effect,并且,每一次的計算都應該使用immutable data
- 必須是純函式,有固定輸入輸出,主要目的是修改自身
- effect
- 主要用于異步請求,介面呼叫之類的
effect被稱為副作用,在我們的應用中,最常見的就是異步操作- 它來自于函式編程的概念,之所以叫副作用是因為它使得我們的函式變得不純,同樣的輸入不一定獲得同樣的輸出
- subscription
subscription語意是訂閱,用于訂閱一個資料源,然后根據條件dispatch需要的action- 資料源可以是當前的時間、服務器的websocket連接、keyboard輸入、geolocation變化、history路由變化等等
- 內部定義的函式都會被被執行,執行之后作為監聽來處理事務
- dispatch
dispatch是一個用于觸發action的函式,action是改變state的唯一途徑,但是它只描述了一個行為,而dipatch可以看作是觸發這個行為的方式,reducer則是描述如何改變資料的- 在
Dva中,connect model的組件通過props可以訪問到dispatch,可以呼叫model中的reducer或者effects
import { connect } from 'dva'; const testCom = props => { const { dispatch } = props; const changeValue = https://www.cnblogs.com/fx67ll/archive/2022/10/20/(id, val) => { // 呼叫reducer,一般是同步修改state中的值 dispatch({ type:'dva/save', payload: { param: val }, }); // 呼叫effect,一般是發送后臺請求 dispatch({ type: 'dva/queryValue', payload: { id: id }, }); }; return( <div>'hello world'</div> ) } export default connect(({ dva }) => ({ ...dva }))(testCom);
Model中的Effects函式決議
需要注意的是:Effects里面的函式都是Generator函式
- yield
- 固定關鍵詞,
Generator函式自帶的關鍵詞,和*搭配使用,有點像async和await,使用*則表明它是Generator函式 - 然后每使用一個
yield就是告訴程式這里是異步,需要等待這個后面的代碼執行完成,同步代碼可不使用該關鍵詞
- 固定關鍵詞,
- payload
- 頁面上通過
dispatch傳過來的payload同名引數
- 頁面上通過
- select
Dva中Effects函式的固定傳參- 用于拿到
model中state的資料,需要注意的是,state后面跟命名空間namespace的值
const data = https://www.cnblogs.com/fx67ll/archive/2022/10/20/yield select((state) => state.namespaceName.valueName); - call
Dva中Effects函式的固定傳參- 第一個引數是一個異步函式,
payload是引數,可以通過call來執行一個完整的異步請求,又因為yield的存在,就實作了異步轉同步的方案
const { data } = yield call(queryInterface, payload); - put
Dva中Effects函式的固定傳參- 可以使用同
model中的Reducers或者Effects,通過Reducers來實作資料到頁面的更新,也可以通過put實作Effects的嵌套使用
yield put({ type: 'save', payload: { ...payload }, });
開發目錄
由于公司的fish框架以及常見的umi框架都對Dva做了深度繼承,會默認將src/models下的model定義自動掛載,只需要在model檔案夾中新建檔案即可新增一個model用來管理組件狀態,對于某個page檔案夾下面的model也會默認掛載
├─assets `靜態資源`
├─components `公共組件`
├─config `路由和環境配置`
├─constants `全域靜態常量`
├─locale `國際化`
│ ├─en_US `英文配置`
│ └─zh_CN `中文配置`
├─models `全域資料狀態` *Dva涉及的目錄*
├─pages `頁面目錄,用我參與開發的其中一個目錄來作為示例` *Dva涉及的目錄*
│ ├─NodeConfig `NodeConfig示例目錄`
│ │ ├─components
│ │ │ ├─Select `Select組件頁面檔案` *Dva涉及的目錄*
│ │ │ │ └─components
│ │ │ │ ├─AudienceInfo
│ │ │ │ │ ├─index.js
│ │ │ │ │ └─index.less
│ │ │ │ ├─BlackList
│ │ │ │ │ ├─index.js
│ │ │ │ │ └─index.less
│ │ │ │ ├─ControlGroup
│ │ │ │ │ ├─index.js
│ │ │ │ │ └─index.less
│ │ │ │ └─GroupSelect
│ │ │ │ │ ├─index.js
│ │ │ │ │ └─index.less
│ │ │ │ ├─index.js
│ │ │ │ └─index.less
│ │ ├─models
│ │ │ ├─select.js `Select組件資料狀態管理` *Dva涉及的目錄*
│ │ └─services
├─services `全域介面配置`
├─themes `全域樣式主題`
└─utils `js通用工具`
PS: 該樹形圖通過 `windows shell` 自帶的 `tree` 命令生成
如何使用Dva
首先定義一個簡易的model示例
export default {
namespace: 'dva',
state: {
id: '',
value: {},
},
effects: {
// 所有effect前必須要加 *
*queryValue({ payload }, { select, call, put }) {
const params = {
id: payload.id ? payload.id : yield select(state => state.select.id)
}
const { data } = yield call(queryInterface, params); // queryInterface是定義好的后臺請求介面,一般用axios或fetch來完成
yield put({ type: 'save', payload: data });
},
},
reducers: {
save(state, { payload }) {
return {
...state,
...payload,
};
},
},
subscriptions: {
keyboardWatcher({ dispatch }) {
key('?+up, ctrl+up', () => { dispatch( {type:'save'}) });
},
},
};
然后把model和組件系結在一起
React的Connect函式是一種柯里化寫法
import { connect } from 'dva';
const testCom = props => {
const { helloWorld = 'hello world'} = props;
return(
<div>{ helloWorld }</div>
)
}
// 系結之后就可以在testCom組件中使用命名為dva的model了
export default connect(({ dva }) => ({ ...dva }))(testCom);
柯里化
柯里化是把接受多個引數的函式轉換成接受一個單一引數的函式(PS:Scala語言中也有類似的設計)
// 柯里化
var foo = function(x) {
return function(y) {
return x + y
}
}
foo(3)(4)
// 普通方法
var add = function(x, y) {
return x + y;
}
add(3, 4)
無狀態組件
創建無狀態組件是為了創建純展示組件,這種組件只負責根據傳入的props來展示,不涉及到要改變state狀態的操作,
在實際專案中頁面組件被寫成無狀態的組件,通過簡單組合可以構建成頁面或復雜組件,通過多個簡單組件來合并成一個復雜的大應用
const NoStateComponent = props => {
const { helloWorld = 'hello world'} = props;
return(
<div>{ helloWorld }</div>
)
}
export default NoStateComponent;
無狀態組件的優點
- 由于是無狀態組件,所以無狀態組件就不會在有組件實體化的程序,無實體化程序也就不需要分配多余的記憶體,從而性能得到一定的提升
- 代碼整潔、可讀性高,對于大型專案的開發維護非常有好處
參考檔案一 ———— Dva官方檔案
參考檔案二 ———— UMI官方檔案
參考檔案三 ———— REACT基礎筆記 MODEL分層
參考檔案四 ———— 前端資料流方案Dva
參考檔案五 ———— 淺析dva (史上最全的dva用法及分析)
參考檔案六 ———— 【dva】model中effects函式的決議
參考檔案七 ———— Generator 函式的詳解
參考檔案八 ———— React connect()() 雙括號 --柯里化寫法
參考檔案九 ———— 高級函式技巧-函式柯里化
我是 fx67ll.com,如果您發現本文有什么錯誤,歡迎在評論區討論指正,感謝您的閱讀!
如果您喜歡這篇文章,歡迎訪問我的 本文github倉庫地址,為我點一顆Star,Thanks~ ??
轉發請注明參考文章地址,非常感謝!!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/518777.html
標籤:其他
上一篇:react-專案結構
