感覺redux寫起來很麻煩,目前有那些其他的狀態管理方案?
後端儘可能把數據打包好,嵌套化。前端
用了redux又要把數據範式化,層層解包。感覺這種方式很兜圈子…從獲取數據到到UI渲染,redux通常要七八個步驟,感覺太繁瑣了。
有那些更為簡潔好用的方案嗎?
或者儘管它很繁瑣,你仍然堅持用它的理由是什麼?
推薦一下我在用的並且參與貢獻的框架 Bucklescript-TEA,用 BuckleScript 寫的 Elm 框架,不同於 redux/dva 這種用 JavaScript 寫的仿/類 Elm 框架,因為擁有 OCaml 的強大表達能力,完全能夠實現原版 Elm 極其簡潔的 API ,這就 address 了大家提到的在 redux 裡面隨便加個 reducer 也非常麻煩這個問題。另外比起原版 Elm, Bucklescript-TEA 因為利用了 BuckleScript 強大的 JavaScript 支持,可以很簡單就把非同步代碼 enqueue 到 Elm 的 Task 隊列,而這正 address 了大家提到的 redux 對非同步支持不好的問題。
鏈接:OvermindDL1/bucklescript-tea
Redux是基於純函數的,為了保證它的「純度」,它的reducer函數必須是嚴格的 S" = f(S) 的形態,所以,與其說Redux是「狀態管理」庫,不如說它是「狀態轉移管理」庫,因為Redux是無狀態的,狀態是在你的程序里的,你自己維持狀態,它只是給你提供了一個狀態轉移的統一方式。這使得它的整個模型看起來是非常乾淨。
而事實上我們在開發實際項目當中可能有小半(maybe大半)的reducer場景其實應該是 S" = await fAsync(S) 的形態,比方說,我點了一個計數器+1的按鈕,在一個美麗的DEMO里,它就+1了,但放到生產需求里,很可能是要先發起一個Ajax請求,請求OK了再+1,甚至這時候不是+1,而是直接和服務端同步一個新的值。
但非同步的reducer就破壞了它的「純度」,因為非同步是不確定的,先發不一定先至,這會破壞reducer的「可回放性」,它引以為豪的replay就不成立了,它的基石就崩塌了。
這就註定了它解決不了非同步的問題,然後它為了讓自己顯得很白玉無瑕,死活也不願意碰非同步那攤子髒東西,什麼?你要在reducer里發起一個Ajax請求?對不起這不是我們的best practice,我們對此嗤之以鼻,你如果真要這麼做,那就……做去吧。
於是Redux在解決非同步問題上的「殘疾」就註定給它擦屁股的庫會如雨後春筍一樣的湧現出來,比如其他回答里提到的dva,再比如redux-saga,它們都是勇士,做了redux所不願意做的那攤子臟活。
這個過程又引入了新的麻煩,一方面是非同步本身帶來的複雜度,比如redux-saga里的every和latest;另一方面是代碼寫起來的麻煩,比如redux-saga里各種yield,還有可以堆成山的膠水代碼。
所以你覺得redux寫起來很麻煩,麻煩就對了,這不是幻覺,因為它為了保證自己的簡潔,把麻煩的事情拋給了你。結論很簡單,要麼就引入更多「生態」,讓別人幫你解決麻煩,讓它們對你輸出價值觀。要麼就別走redux的函數式路線,去你大爺的純函數,用別的價值觀,我用全局變數全局事件,用watch,用observable,用whatever,反正不用你。
在我看來,目前主要有兩個方向:
一是將 Redux 進一步封裝,來達到簡化的目的,可以一定程度上針對自身的業務場景,例如:dva、refect、refast 等等
二是採用 observable 的方案,例如:MobX、dob 等等
要不要支持一下國產的 dob?
理念與 mobx 相同,使用 proxy 讓使用過程更流暢,數組不會搞成純對象,有依賴注入最佳實踐,還能配合 redux。
@淡蒼 已經說得很詳細了,我補充點東西。
數據的更新,從本質上講,就是兩條路:immutable,observable。目前流行的狀態管理方案,甚至前端組件框架,都是在這兩條路上做文章。
我們可以看到,在純正的原生 redux 之外,又出現了各種各樣的封裝,最典型的是 dva,除了在 action 的 create、dispatch 等方面做約定式簡化,大部分都引入了某種 model 的概念。
但是 model 這個東西,本身跟 redux 的理念是不完全兼容的。基於 redux 的這套設計,實際上是基於 cqrs 方法論,數據作為被操作對象,其他東西都是操作數據的動作。談到 model,大家又都是傾向於往 ddd 的方向去思考,把某些領域的數據跟其相關的操作打包起來。但嚴格按照領域設計的 model,會出現一些級聯更新所導致的事務問題,比如說,有一個操作要處理跨 model 的更新,這種情況很難避免,也用不著每次為了這個事情去調整 model 的設計。
所幸的是,由於 react-redux 這個庫的實現機制,這個事務問題是正好被處理掉了的,所以,dva 的運行過程沒有什麼明顯不合理的地方。其他一些嘗試基於 observable 做類似封裝的,需要注意一下這個事情,要用其他機制,比如合併更新隊列的方式去處理。
我們團隊流傳著一句話:redux 是讓自己爽,噁心別人;mobx 是噁心自己,讓別人爽
另外,下一個版本的 mobx-state-tree 值得期待下。Introduce new syntax to declare actions and volatile state · Issue #282 · mobxjs/mobx-state-tree
API 是不是有種相似的感覺~門檻越來越低了
別想了,用rxjs梳理事件流,然後模型自己隨便開對象維護,反正客戶端模型一般也是單例
為了讓 Redux 用起來像 vuex 一樣簡單,於是我們簡單封裝了下,寫了這個庫 https://github.com/d-band/yax
難和容易是個不好界定的東西,不談場景都是耍流氓
API少 -&> 看起來學習簡單 -&> 應用構建複雜
API少 -&> 功能缺失,自己造,苦逼,但同時可擴展性好 -&> 容易定製
API少 -&> 介面基本不變(帶不帶表底層簡單) -&> 遷移容易 -&> 迭代良好API多 -&> 看起來學習困難 -&> 中小應用簡單,快速上手,大型應用依然複雜
API多 -&> 本身介面豐富 -&> 很多功能開箱即用,真的很爽 -&> 可否擴展? 再擴展是否重複?API多 -&> 升級變化可能較大 -&> 要想支持新版功能,版本遷移和迭代就苦逼了都各自有好處和弊端,舉些例子
React vs Vue vs Angular
Redux vs MobxReact + Mobx vs VueReact + Redux*** vs Dva在設計庫的時候,盡量做到開箱即用(直接食用涉及API少),讓使用者無痛,在約束範圍內盡量提供更多更多可用API,但是避免暴露所有數據造成不可控的情況。然而這比較難,考慮的要更多,這是我個人設計庫的原則,僅供參考。Redux本質上就是把問題分解成很小的、規範的部分,這個過程顯得冗餘(verbose),所以看起來的確麻煩,但是好處就是,每個很小的部分都很容易理解,不容易出錯,容易單元測試,拼在一起就可以工作得很好。
至於數據的範式化的好處,我在 《深入淺出React和Redux》里講了很多,這裡不多說。最好是後端API傳過來的數據就範式化好了,不然用 paularmstrong/normalizr 之類的工具也不是特別麻煩(既然都用上了Redux這樣麻煩的工具:-)
要說更簡潔的工具?最簡潔的莫過於玩裸的組件,無論是React還是Vue,庫本身都具有一定的數據管理能力,只要開發人員有一點紀律性,直接上React不用任何框架也不是大問題,只是,Redux這樣的框架不就是強制執行紀律的嘛,人多了、項目打了,光靠人自覺是不行的。
現在除了Redux還有另一個方向的MobX也可以嘗試一下,個人覺得MobX管理大型項目能力稍遜Redux。
話說回來,上帝是公平的,給你打開一扇窗,必定會關上一扇門。沒有哪個框架是十全十美的,有優點就必有缺點,永遠不要相信只吹噓某個框架只有好沒有壞的人。
如果你喜歡這些詞: FP, FRP, observable, declarative, monadic, composable, reactivex, fantasyland
可以來試試 https://github.com/reactive-react/xreact看到很多人推薦 mobx,我更想推薦一件利器 MST - mobx-state-tree。誰用誰知道
目前我就用redux,但是用最簡化的方式。很少用函數額外包裝一下。
作為架構師,我告誡團隊只在必要時使用redux,另外代碼盡量簡練,能合併的信號就合併。
項目第一期已交付,經理客戶以及程序員對結果和流程都很滿意。
redux只是一個庫而已,如何使用是你的自由,所謂「最佳實踐」充其量只是一面之詞。
技術上很簡單,沒有什麼好多談的。
坦率地說,Usage with React (Redux usage)我看過了,我認為其所推薦的actions的寫法不適用於中大型項目。
函數式編程是一種很有用的技巧,但也只是在適當的場合使用,Usage with React里例子實在是過於繁瑣了。
我不是函數式編程理論專家,但是Lisp也用了好多年,Lisp開發的幾個軟體在Emacs社區里下載量也名列前茅,可稱得上精通函數式編程常用技巧。所以我對於Usage with React完全是抱著批判的態度看完的。
建議你多參與一些開源項目,對技術和人有所了解,這種問題就自然很簡單了。對技術認同並不等於必須盲從其開發者和「專家」。
作為用戶,如何使用技術是你的自由。你完全可以基於你自己的理性選擇和「最佳實踐」不同的道路,不用管這些最佳實踐是哪個權威推薦的。你也可以混用各種技術,不必非此即彼,這也是你的自由。
目前很多中國程序員的問題是對於開源項目貢獻度不夠,只是拿來用。看起來佔便宜,實際上吃大虧。因為參與度不夠,所以態度上對老外都有盲從和敬畏的傾向。要實現「拿來主義」當然都是紙上談兵。
一直不愛用redux以及它的全家桶,之前用的reflux,相對於redux靈活多了,也簡化剁了,但是寫項目沒有固定範式,效果也是參差不齊。
然後入坑mobx,講真這個接觸時間不算久但是還是可以試試的,尤其是結合ES7包裝器語法糖之後。但是mobx是基於observable的模式,很難說大項目適合,比如多個數據源,各種混搭流……估計會一團亂麻,相比之下,redux及其全家桶在這方面就考慮的很周到了,畢竟有dispatcher,reducer可以很好地對狀態、數據流進行隔離……當然,項目得複雜到一定程度……
最近新入坑了一款簡單到極致的 React 數據流框架——Refast,西廠出品的,倒是很不錯,它是基於redux的思想做了大幅簡化的,比mobx用著爽,答主可去試試。
推薦用 mobx https://suprise.gitbooks.io/mobx-cn/content/fp.html
剛開始用react時也是被網上各類文章、知乎回答引導著強行去用redux。等後來熟練了,對HOC和context比較熟悉了之後,後面的項目大大小小再也沒感覺到用得上redux。最多也是自己簡單封裝個provider下髮狀態,也沒幾句代碼。大多數的公共數據狀態通過高級組件和context都可以解決。
不分場景業務強上redux就是把簡單的事情複雜化了。大類就兩種: Redux 、MobX
Redux 中非同步需要中間件支持:redux-thunk / redux-saga / redux-observable 等等
具體可以看我最近總結的 demo:riskers/data-flow,裡面用 使用 redux-thunk、 redux-saga、 redux-observable、 MobX製作同樣的一個小項目:
如果你總是想著「狀態管理」,覺得自己要解決「狀態管理」這個很嚴肅的問題,你大概就會在錯誤的道路上越走越遠。
JS大程序的架構只有兩個根本問題:
- 如何模塊化
- 模塊間如何通信
狀態應該在模塊內處理掉,Redux存在不是「替」模塊管理狀態,而是在模塊充分封裝狀態的「前提」下,方便他們之間對話。看上去store里存了很多狀態,似乎在剝奪模塊做這些事的權力。但出發點是通信,你看到的不是什麼設計,而是副作用。
我想起在jQuery時代我可以不用害羞地寫出一個個封裝良好的插件,對等於一個個component,和DOM無縫集成。
而且,它們之間通信,不強迫模塊交出狀態,我只要懶懶地:
window.EventHub = $({});
window.EventHub.on("event-name", fn);
window.EventHub.trigger("event-name", {...});
就覺得十分美好,這種感覺,你們開口一堆術語的人,是不會懂的。
我這麼說,是因為我不假設Redux是「對」的。
不錯,Redux剛上手一用是很繁瑣,我們團隊里也有過爭論是否要用Redux. 明明一些很簡單,call一句setState就可以搞定的邏輯,偏偏要搞綁定,要定義action, 要寫reducer,要mapPropsToStates, 外加dispatch。這不是吃飽了撐的么?
但是,這些代價都是值得的!因為Redux實現了兩個非常重要的目的:
1. 組件無狀態。
2. 實現Flux模式(已更正),定義單一數據流向先說第一個。React里同時有props和states可以說是React設計的一大敗筆。Props和states的同步如果做得不好,後患無窮。而且一個有狀態的組件本來就是一個麻煩事,難寫難單元測試。後來Facebook自己也推薦無狀態組件。Redux更近一步,拋棄了states,從框架上解決了props/states不同步問題。
第二個。單一數據流向太重要了。UI項目最致命的幾個問題,一是數據源和顯示不同步,二是數據變更通知不到位。這兩個問題最短平快的解決方式就是在UI組件里接受用戶數據響應的代碼里直接改數據對象和其他UI組件的狀態。這種編碼最直觀最快,但結果就是UI組件之間強耦合,以及產生不內洽的數據。於是稍微高級點,大家開始使用Observer模式(比如iOS上的NotificationCenter)。Observer模式解決了強耦合的毛病,但Observer沒有閉環,無法解決數據源和顯示不同步的問題。當然,你可以雙向實現Observer,但又引發的問題就是數據通訊通道變得像蛛網一樣複雜。雙向Observer的極致就是自動雙向數據綁定(比如AngularJS),好是好(絕對比redux容易上手),但一旦濫用,你都不知道數據更新死循環是哪裡來的。
而Flux模式(Redux實現Flux,但沒有redux你也可以寫flux, 實現很簡單)看似彎彎繞,要改個狀態還要跑到外面繞一圈,但其實好處太多了。數據流動只有一個單向通道。數據來源和任何組件無關(解耦)。數據處理集中在一個地方,處理由純函數定義,避免各組件各自亂改數據導致不自洽。組件無狀態,組件的表現完全取決於輸入數據(props),極易單元測試。
總結下,個人覺得UI編碼是最重設計模式的編碼,一定要花時間在infrastructure code上面,定好模式和規矩。短平快地「那麼彎彎繞幹嘛直接改不就行了」只能是一時爽,以後這堆義大利麵條代碼能整死你。除非你寫的是拿參數的純命令行程序。Redux慎用,一方面,小項目使用Redux更多的是一個累贅,用react-actor, mobx這種消息模型已經足夠了;但是對於數據龐大複雜的項目,使用函數式編程對可維護性的便利是很大的,但是不一定要用Redux這麼噁心的方式來實現,dva應該不錯。
推薦閱讀:
※redux 中的 state 樹太大會不會有性能問題?
※redux中所有state都要放在store里嗎?
TAG:前端開發 | JavaScript | 前端工程師 | React | Redux |