在 React 應用中,狀態管理是一個沒法繞開的話題,對於開發人員來講,如何優雅地管理狀態是一個比想象中要複雜的事情,是一門真正的開發藝術。前端
衆所周知,React 應用是由一個個狀態機組合而成, 組件是 React 應用的核心,狀態則是一個或者多個組件的靈魂。對於一個成型的 React 應用來講,組件構成了應用的骨架,而狀態則是貫通全身的血液是應用充滿活力和生機的關鍵。vue
隨着應用的複雜化,要準確區分哪些組件須要狀態將是一件無比困難的事情。若是將全部狀態都存放在根組件,經過 context api
來消費狀態未免不是一種簡單的方法。但這並不能有效地解決問題,由於咱們不只須要經過 setState
細粒度控制狀態變化,還要設計各組件間的狀態通訊。一旦設計很差,狀態的管理可能會變得混亂,狀態的變化也會由於無跡可查而變得不可預測。react
Redux 做爲社區最流行的狀態管理方案,爲咱們提供了可預測的狀態容器。在 Redux 中,應用的狀態被儲存在一個 js 對象中做爲單一數據來源,能夠隨時被獲取。將咱們的注意力集中在這個對象上,排除了其餘環節的開發和維護成本。而且應用的狀態是「只讀」的,不能直接修改狀態。修改狀態的惟一方法是派發(dispatch)相應動做(action)交由相應的純函數(reducer)來更新狀態,而後返回一個全新的狀態對象交由頁面渲染。git
經過上述約定,在 Redux 中,每個狀態的更改都是有跡可循,有據可查,這對咱們狀態管理的編程體驗和代碼維護以及後期 bug 排查極其重要。Redux 自己做爲一個訂閱發佈系統,具體的內部處理流程以下:github
Redux 確實將狀態管理簡單化,合理化,但做爲一個精簡的庫,它卻有着陡峭的學習曲線,使人望而生畏的函數式編程概念和使人一頭霧水的概念,讓衆多開發者望而卻步。並且在實際的開發中,Redux 的開發和維護也是一件很繁瑣麻煩的事情。vuex
Rematch 的做者 Shawn McKay 曾在 《Redesigning Redux》 中對 Redux 提出了 6 個可改進之處,但具體可歸納爲如下 3 點:npm
上述問題看似日常,但與咱們平時的狀態管理息息相關。由於這些問題,Redux 不但不會提升咱們的工做效率,甚至會下降工做效率。但 Redux 的狀態管理思想是正確的,實際的項目也須要這種思想。所以爲了更好地使用 Redux,下降其學習和使用成本,咱們急切須要基於 Redux 的最佳實踐。編程
隨着 Redux 使用程度的加深,其暴露出的工程化問題就越發嚴重。社區內存在很多解決 Redux 工程化問題的方案,其中 Dva 可謂是最亮眼的那顆星,被業界稱爲是基於 Redux 的最佳實踐。redux
Dva 是基於 Redux 現有應用架構(redux + react-router + redux-saga 等)的一層輕量封裝,幫咱們自動化處理了 Redux 架構中繁瑣的步驟,好比樣板代碼維護、store 的建立和中間件的配置,將原來那些噁心、繁瑣、容易出錯的步驟掩藏起來,用簡潔明瞭的 api 來進行配置和控制。api
函數式編程概念繁多
這部分問題主要集中 Redux 的初始化上,Dva 經過基於對象配置的方式解決初始化函數組合的問題,具體如圖:
樣板文件
在樣板文件上,Dva 只解決了 reducer 和 redux-saga 樣板文件的自動化管理,並無解決 action 的樣板文件管理,所以這也算 Dva 的一個缺點吧。
model 是 Dva 的重要概念,對 Redux 社區有着不小的影響,它將 Redux 下的每個子狀態的下的 state、reducer 和 sagas 都寫在一塊兒,經過對象配置的方式使 Redux 樣板文件的管理思路清晰簡單。下面是一個簡單的例子:
Dva 將 action 分爲同步 action 和 異步action,同步 action 的處理方法定義在 reducers 屬性中,是惟一能夠更新狀態的地方。異步 action 的處理方法定義在 effects 屬性中,同時也能夠派發同步 action。
在註冊 model 時,Dva 會按照 reducers 中定義的方法建立 reducer,按照 effects 中定義的方法建立 saga 反作用。
異步處理問題
在異步動做的處理問題上,Dva 選擇了 redux-saga。Redux-saga 能夠更容易管理反作用,執行更高效,測試更簡單,在處理故障時更容易。經過 generator 來定義處理異步方法,讓異步的流程更易於讀取和寫入,而且能夠很容易地測試異步流程並保持 action 是純潔性。
插件機制
在解決 Redux 工程化問題的同時,Dva 提供一套靈活的插件機制,讓開發者能夠深刻 redux 數據流處理的每一部分,進一步提升代碼的靈活性和工做效率。
即便被奉爲最佳實踐,Dva 依舊也由於客觀因素和侷限性存在如下缺點:
actionTypes
,actionCreators
相關維護Dva 以後,社區出現了許多基於 Redux 封裝的狀態管理框架,但大多存在不小的侷限性,甚至倒退,直到 rematch 的出現讓人眼前一亮。rematch 是一個受 Dva 啓發,零 boilerplate 的 Redux 實踐,風格與 vuex 類似。
rematch 一樣採起配置化 + model 的方案,將 redux 的實踐,封裝的更加簡單。在設計上,rematch 與 Dva 仍是有些不一樣的,咱們來看一個完整的例子:
rematch 與 Dva 的不一樣在於,rematch 只是一個狀態管理框架,只負責狀態管理,其餘一律無論,沒有內置過多的庫。而且建立應用步驟更少,減小了無謂的過程式調用,能經過配置毫不調用函數。
在總體使用風格上,rematch 更加輕便,將 redcuer、saga 和 action 統一使用 model 維護,redcuers 的定義更是合二爲一,在定義 reducer 的各 action 處理方法時,便將對應的 actionCreator
和 actionType
定義下來,同時能夠經過對象屬性訪問直接派發 action,減小了樣板代碼,極大程度上下降咱們的代碼成本。
在異步問題上,rematch 使用 async/await
替代了redux-saga
和 thunk
方案,下降了異步動做處理的理解和使用成本。
在插件生態上,rematch 擁有相對較強的插件生態概念,將經常使用的 reselect
、persist
、immer
等都集成爲了插件。在數據緩存,性能優化,開發體驗優化都有進一步施展的空間,畢竟擁抱插件生態是一個良好的發展方向。
從 Redux 到 Dva 再到 rematch,咱們能夠看到 Redux 開發從刀耕火種到自動化的進步,狀態管理變得簡單高效,但總歸來講,最佳實踐方案基本上都是對 Redux 進行封裝,在提供更簡單的 API 的同時又不失任何可配置性的特色。
在看到社區的進步時,同時咱們也感覺到一絲無奈。無奈何處?那就是看着這麼香,這麼高效的工具,想從已有狀態管理框架遷移至新的管理框架卻存在不小的鴻溝。好比咱們想從 Redux 現有應用架構(redux + react-router + redux-saga )切換至 rematch,則必須捨棄 redux-saga,改變咱們原有的異步處理方案,存在不小的遷移成本。 所以形成了目前新項目自動化,老項目累死累活的局面。這不是咱們想看到,也是不肯意看到的狀況。
rematch 啓發於 Dva,可謂已經將 Redux 封裝到極致,但這卻只是對 Redux 工程化問題的解決,而在實際的狀態管理的業務代碼上依舊有着客觀的優化空間,好比說:批量建立 action 處理方法。
唐刀
爲了使盡量多的項目(不管新老項目)都能用上與 rematch 同樣的的狀態管理框架,最大可能地提高研發效率,下降研發代價,咱們決定從新打造一個基於 Redux 的狀態管理框架。
咱們貓眼前端團隊對社區已有方案並結合實際狀況進行了長時間地調研和分析,決定保留 Redux 現有的應用架構(redux + react-router + redux-saga ),達成較低的學習和上手成本,能夠與原生 redux 數據流管理並存,Dva 狀態管理框架無縫遷移,rematch 快速遷移的目標。而且將 react hook 歸入後續規劃,準備打造基於 react hook 版的狀態管理周邊生態,進一步提升狀態管理能力。
通過長時間開發與屢次優化,咱們終於打造出一款符合上述要求,並在業務代碼有必定優化的狀態管理框架—— 唐刀。
唐刀是一款汲取 Dva 和 rematch 精髓的數據流管理工具,作到了零 boilerplate,插件生態豐富,能夠與原生 Redux 數據流管理方式共存,Dva 項目無縫遷移,rematch 項目快速遷移,並在業務代碼上做出了可觀的優化,極大地提升了狀態管理效率。咱們來看下,唐刀完整例子:
爲了達到 Dva 項目無縫遷移,唐刀在設計理念和 api 設計上基本與 Dva 保持一致,平移支持了 Dva 的核心功能,並針對 dva 的不足進行了修正與補充,下降 dva 用戶的學習和上手成本。在樣板管理和 action 派發上借鑑了 rematch,爲 Dva 項目和 rematch 項目快速遷移至唐刀提供了可能和保證。
Generator 與 async/await 的抉擇
在框架的高度封裝下,對於用戶來講 Generator
與 async/await
只是語法糖上的區別,寫法不一樣但功能一致,sagas 反作用的建立由唐刀內部實現,開發者對此是無感知的。所以,對於 rematch 用戶,這部分只是在寫法上的不一樣,不須要作過多的改動,除此以外沒有其餘的不一樣。
初始化
爲了能夠與 Redux 數據流管理方式並存,在初始化時,唐刀提供了相關屬性用以配置 redux 架構中的各類工具。這樣即可以達成先接入,後改造的局面。具體以下:
經過屬性配置,你徹底不用去關心和理解這些屬性的實際調用過程,保證了快速接入項目的可能性。
插件
唐刀也是積極擁抱插件生態,在將來咱們還準備打造基於 react hook 豐富數據流管理的周邊生態,目前唐刀內置並提供瞭如下插件:
在狀態管理的相關業務代碼上,針對寫法相同,只是更新屬性不一樣的 action 處理方法,唐刀提供了批量創造 action 處理方法的屬性,讓你的 model 代碼變薄,少寫更多的代碼。具體以下:
從上述對比能夠看出,唐刀不只要把狀態管理簡單化,高效化,還要儘量兼顧到已有方案的遷移,讓儘量多的項目擁抱簡單高效的狀態管理。在這一過程當中,咱們沒有貶低任何方案,儘量作到取百家之長,以更低的學習成本,更少的樣板代碼和更少的學習成本,來擁抱 Redux 背後的簡單哲學。
若是你還在備受 redux 架構或者其餘狀態管理工具的折磨,那麼不防嘗試一下唐刀,說不定它會給你帶來意想不到的收穫,心動不如行動,早用早解脫。
最後的最後,小編賣身求 star,小哥哥小姐姐們給我點它!
Github:github.com/MaoYanTech/…