redux 有什麼缺點?
公司在用自己的虛擬dom 框架,但是我覺得沒有一個類似redux的框架管理狀態,不一定是一個好的選擇。但是周圍幾個前段大神都覺得redux有致命缺點,但從我的角度真的沒有找出什麼缺點,求知乎里的前段大神指點。
謝邀,這是一個很好的問題。
即便很多 「Redux」 死忠或者 「Dan Abramov」 腦殘粉,回答到 Redux 缺點時,也一定能夠客觀:因為技術的歸技術,真正的缺點永遠無法被掩蓋。
但是我認為:對於任何技術設計、思想框架,與其說他們的優缺點,不如用「適合不適合」某類場景來表達更加準確、客觀。
事實上,優缺點會隨著業務場景而動態變化。所謂「甲之蜜糖,乙之砒霜」,脫離實際場景談優缺點是毫無意義的。對於 A 的業務,Redux 完美契合,對於 B 的產品,也許就是桎梏枷鎖。
所以下文,主要回答 Redux 的限制或 tradeoffs,原諒我無法使用「缺點」來表達。
其實,Dan Abramov 很早就提到過 「You might not need Redux」,文中提到了 Redux 的限制。他也說過 「Try Mobx」 這種「打臉」行為。歸納一下,Redux 的限制主要體現在:
- Redux 帶來了函數式編程、不可變性思想等等,為了配合這些理念,開發者必須要寫很多「模式代碼(boilerplate)」,繁瑣以及重複是開發者不願意容忍的。當然也有很多 hack 旨在減少 boilerplate,但目前階段,可以說 Redux 天生就附著繁瑣;
- 使用 Redux,那麼你的應用就要用 objects 或者 arrays 描述狀態;OMG!
- 使用 Redux,那麼你的應用就要使用 plain objects 即 actions ,來描述變化;OMG!
- 使用 Redux,那麼你的應用就要使用純函數去處理變化;OMG!
- 應用中,狀態很多都要抽象到 store,那麼何時使用 local states 何時接入 Redux store?
- 不能痛痛快快地寫業務,一個變化就要對應編寫 action(action creator),reducer 等等;
- 和響應式結合函數式的 Mobx 相比,編程體驗「打折扣」
我在額外追加一條:
Redux 可以理解為一個簡易的發布訂閱系統。那麼因此帶來的內存消費也許會大一丟丟。
以上種種限制,都不是構建一個應用所必要的。而全部都是 Redux 所強加的,那麼這樣好嗎?
對於很多應用,是沒必要的。但是對於另外一些場景,這些限制也都會轉化成閃光之處,缺點彷彿又成了優點,包括且不限於:
- 便於調試,具體不再展開;
- 便於線上錯誤收集,只需要發送 states, actions 等快照即可;
- 結合 localStorage 初始化 store;
- 便於服務端渲染;
- 開發在線協作型應用的救命解藥;
- 時光旅行 Undo/Redo;
- 便於測試
看個場景吧(取自 Dan Abramov, 沒耐心看可直接看結尾總結),一個簡單的計數使用基本 React:
import React, { Component } from react;
class Counter extends Component {
state = { value: 0 };
increment = () =&> {
this.setState(prevState =&> ({
value: prevState.value + 1
}));
};
decrement = () =&> {
this.setState(prevState =&> ({
value: prevState.value - 1
}));
};
render() {
return (
&
{this.state.value}
&
&
&
}
}
分析代碼,我們完全沒必要使用 Redux 對不對,可是你們看 decrement 和 increment 貌似有一些冗餘代碼,如果我們將「發生變化」和「變化引起的效應」從這個狀態組件中解耦:
import React, { Component } from react;
const counter = (state = { value: 0 }, action) =&> {
switch (action.type) {
case INCREMENT:
return { value: state.value + 1 };
case DECREMENT:
return { value: state.value - 1 };
default:
return state;
}
}
class Counter extends Component {
state = counter(undefined, {});
dispatch(action) {
this.setState(prevState =&> counter(prevState, action));
}
increment = () =&> {
this.dispatch({ type: INCREMENT });
};
decrement = () =&> {
this.dispatch({ type: DECREMENT });
};
render() {
return (
&
{this.state.value}
&
&
&
}
}
這是 Redux 嗎?不是,我們不用 npm install 就可以運行呢,但這就是 Redux 某種樸素的思想之一啊。
那麼如果是我,會這麼寫嗎?Definitely not,但是你試圖揣摩一下這樣編寫代碼的出發點,也許會越想越有意思。
同時,我在 GitHub 上還發現過這種奇怪的東西:noredux,這個庫把 Redux 簡化為:
const reducer = (state)=&>state + 1
store.dispatch(reducer)
額......interesting!
總結:
Redux,它是天使,也是魔鬼。它帶來了無與倫比的調試激情和開發體驗,也帶來了無法遮蓋的樣板代碼和重複勞動。它是寵兒,也是棄兒,他在社區里贏得尊重,他在尊重中「自我否定「而又反哺社區。
一些開發者總是鑽著牛角尖,數著優缺點;卻總有一部分人在優缺點中看利弊,心中早已有了自己的答案。
廣告時間:
如果你對前端發展,尤其 React 周邊感興趣:我的新書中,也許有你想看到的內容。
聯繫我,完全免費,想跟你聊聊 React 方面的書,求教大神斧正。
- Pure。這是 Redux 標榜的優勢,讓狀態可預測,方便測試。然而這是一個理想情況,實際情況中全是 Ajax 等各種非同步的副作用,Redux 本身是無能為力的。所以說 Redux 保證了自己的純粹,把臟活累活都給別人做了,衍生出了 redux-thunk,redux-saga,redux-observable 等中間件,非常散亂。
- 啰嗦。畢竟增加了 Action 和 Reducer,所以即便想進行一個小的狀態變化也會需要更改好幾個地方。另一方面,JS 缺乏一個優秀的類型系統,哪怕引入了 TS,其類型系統,模式匹配等也趕 Haskell/Elm 等有比較大的差距。這也導致 Redux 這種函數式寫法顯得非常啰嗦。
- 不是分形(Fractal)的。Redux 架構里,狀態是存在於頂層的一棵樹,通過 Connect 和組件連在一起,單個組件是無法獨立工作的。而分形的架構里,組件內部有自己的狀態機制,單個組件可以獨立工作,也可以作為子組件。而非分形的架構里,視圖可以復用,狀態也可以,卻很難將二者作為一個整體的組件來複用。Elm-Architecture,Cycle.js,Functional-Frontend-Architecture,Meiosis 等都是分形的架構,Redux 卻不是。關於分形,Cycle.js 的作者在 這篇文章 里有詳細的闡述。
順便說下,在 Rx.js 里,Redux 乾的事情也就一個 scan 操作符就解決了。。
按照某RxJS大神的說法,Redux就是為了一次技術峰會而草創的框架,呵呵,這話也不假,不過,當初JUnit也是前輩們坐了一次航班的工夫做出來的,不也一樣很棒而且廣泛被接受嘛,所以,不要用創建的時間和時機來判斷一個框架的優劣。
實事求是地說,Redux是有缺點,當然這些缺點也可以說是獲得一些優點而帶來的缺點:
- 默認只支持同步處理,連作者創造的redux-thunk都是獨立於redux之外的一個包,當然,這樣是讓使用者自行選擇非同步處理方法,但是選擇太多,又沒有各項都見長的解法,所以使用者也抱怨很多;
- 啰嗦(verbose),為了一個功能又要寫reducer又要寫action,還要寫一個文件定義actionType,顯得很麻煩,當然啰嗦就是為了讓一切清晰明確;
- 想湊3條,但是3條實在想不出來。
我個人很喜歡Redux,因為它清晰易懂,沒什麼「黑科技」,這是我個人傾向,當然其他人可以有不同喜好。
使用RxJS可以很容易實現Redux,基本功能只需要下面的代碼。
const store$ = action$.startWith(initialState)
.scan(reducer);
但是,Redux已經有很多成熟的周邊功能,尤其是Redux DevTools,所以,雖然RxJS能夠兩行實現Redux,也不等於說Redux就可以被淘汰,可以用Redux+RxJS的結合Redux-Observable,這貨的缺點是,你必須要學會Redux再學會RxJS,一個RxJS的學習曲線就夠陡的了,呵呵……
個人覺得 Redux 最大的缺點就是雖然借鑒了 Elm 的思想,但是借鑒的卻不徹底。
也許和 React 社區太火爆有關,Redux 依然以 React (視圖庫)為核心,只是把 Redux 當作 React 的一個 全局狀態管理 插件,於是造出了 connect 這種近乎 hack 的狀態綁定語法,弱化了 狀態設計 這一步驟,大多數人依然還是在先寫 React, 然後需要哪個狀態的時候 connect 一下就好,導致狀態和視圖的關係很亂,難以復用。
而且 Actions 和 ActionsTypes, 其實都是在模擬 Elm 的 ADT (代數數據類型)語法,對於動態語言有點冗餘,分散在不同文件夾里導致這點更加明顯,畢竟弱類型語言和強類型語言的套路不同,弱類型對 dry 原則要求更高,寫的重複代碼越多出現 bug 的幾率就越大,而且重構時也容易出現遺漏。
沒有內置的 side effects manager, 導致社區出現了 redux-thunk, redux-promise-middleware, redux-saga 等等中間件,項目的複雜度直線上升。
而這些問題其實在 Elm 中都是不存在的。目前在 js 生態中 1k+ star 以上的項目中只發現 hyperapp/hyperapp 最貼近 Elm 的風格,可惜是一個超輕量級的全功能框架,不支持 React, 而且由於近期的破壞性更新導致很多社區庫都不能工作了,於是這幾天 fork 出了一個 hydux/hydux 自用,參考了一個函數式語言的類 Elm 框架 Elmish 增加了 side effects manager 和 subscribe API,可以通過官方擴展支持 React 和 React Router,內置了大多數社區庫包括 logger, 熱更新,時間旅行(Redux devtools), persist等,以及一個來自 hyperapp 作者的 1kb vdom 庫。
沒辦法阻止不需要它的人用它
加一個有點像抖機靈的回答吧。
redux最大的缺陷其實是它不應該這麼火,或者說不應該由它來火起來。如果是某些整合了redux,非同步管理和immutable等功能的框架來負責火起來,而redux作為基礎庫來支撐它們,就比較容易接受了。雖然造輪子的確有趣,但是實際上一線的開發可不需要什麼百花齊放的輪子……而redux的功能卻太單一了。如果一個庫被用來做狀態管理,我可能就更希望它能完整地hold住這件事情。而不是留下一大堆非同步誰來處理,state對象的不變性怎樣保證這類緊密相關的問題就給我來處理……不是因為我不想動腦子,而是因為一個庫不能自己處理這些問題卻又流行起來的話,市面上過一段時間就會出現十幾數十個奇奇怪怪的方案來處理這個問題……你自己寫久了然後決定根據經驗封裝一個,然後下個項目想換某個據說流行的腳手架,發現腳手架用了另外一個。然後過一段時間社區告訴你,那個腳手架的方案也涼了,現在流行的是另外一個。……得了吧,您可別鬧了,等我搞明白到底該用啥庫時黃花菜都涼了。實話實說,react+自己用類寫狀態管理+rxjs,大部分項目夠用了,項目沒複雜到那個份上之前,何必閑的沒事為難自己呢。
大型項目想很好的駕馭redux還是有一個比較高的門檻的。關鍵還不只是一定會需要很多其他工具來幫助開發,比如immutable,redux thunk。最重要還要明白整套model driven view的思想。就算是一個很有經驗的人,第一次用redux,不好好學習就直接用,一定會有很多坑的(我甚至看到有些評論的思路和我們的就很不一樣,當然每個項目的最佳實踐都不一樣)
隨便舉個例子數據要保存在react state還是redux store?為什麼?
領域模型如何抽象?如何復用?如何保證渲染的高效?更別說我在tx看到的很多項目大量使用ref 使用react.context的用法。在react請求api,在reducer做非同步邏輯等等奇奇怪怪的操作,在迭代的過程中慢慢就走偏了。這些很多時候都是沒有吃透redux的架構的原因導致的。可以說一不小心就掉入陷阱裡面了。
但是只要你們能把整個邏輯正確的搭建起來。就發現萬事都很舒爽了。不能說是缺點吧,不過很多人吐槽的點倒是有:
1.繁瑣。2.store設計,過於扁平化不好管理,但是樹結構過深修改麻煩,需要一定的經驗。
try mobx。個人覺得redux非常好,非常值得學用,對於一個程序員在代碼水平上的提高十分有幫助,配合redux-saga是一個很好的解決方案,saga的問題是使用generator,在babel編譯後只能用polyfill的方式實現,導致調試斷點看起來十分不爽。
但前提是會用,用出redux應有的味道來。
很多說redux難用的,是因為沒有領會redux的精髓,所以越用越繁(煩)。
簡單說,需要比較高的知行合一水準。對編碼、對javascript本身的理解沒有達到一定層次的時候,用redux這種框架去開發,反而化簡為繁,覺得格外累,因為使用redux需要很好的代碼架構能力和對模塊的精細拆分,這些有知識層面的,也有經驗層面的,所以尤其是團隊使用的時候,成員的技術水平不一,往往難以維護一個高質量的代碼水平。
不喜歡淘寶在saga基礎上搞的dva,名字里就透出一股濃濃的山炮味兒。
實際上也可以根據不同的項目使用不同的技術,有些項目急就不宜用redux這種需要斟酌細嚼慢咽去品味的框架,比如有時候使用mobx的確感覺很爽快。
太簡陋和理想,就連最常見的非同步場景都要單獨配個中間件。還有就是基本上正規項目用的朋友應該都做過二次封裝,單純用的我估計蛋疼。目前如果覺得麻煩有 redux 相關的腳手架,或者 mobx 的方式,rx.js+基本代碼組織,都是可以替代的。本來就不應該用裸的,就像 ajax ,中型的項目就應該有封裝了,並且我是指在原有庫的基礎上。
瀉藥,給你說一個事情,我們之前有一個新來的實習生,又一次實習生開始寫我們的業務的時候要用到redux,然後那個實習生寫完了以後問我:"不就是改個狀態么?為什麼要這麼麻煩,要有reducer,要有store等等..."
目前自己用了這麼久,感覺redux還有有一些缺點,首先,你要不停的connect組件,如果項目大規模使用redux的話,需要對數據結構進行構建項目前的模擬,不然哪些數據該存在store里哪些存了反而會麻煩。 其次,你不能吧redux當作一個任何數據都可以存的緩存區,因為最重要一點是每當你reload頁面的時候,你所有存在store里的數據都會被清空,所以一些需要長久存儲的數據比如token,你還是需要放在localStorage里。 總之,redux是一個解決react數據流通的利器,解決了很多痛點,但是要清楚它不是萬能的就行了。
用什麼redux,麵條代碼不好嗎?
我連immutable有時都懶得遵循,直接改state,然後forceUpdate,寫代碼就是一把梭。redux我用了4、5個項目了,一直在找最佳實踐,各種周邊庫都測試過,希望下一個項目可以不用寫那麼多模板代碼,然並卵。而mobx,看完文檔,在知乎看了下文章,然後憑藉薄弱的oop知識,就弄出了一套自我感覺良好的架構了。
1.模式化帶來的繁瑣編程體驗,編程體驗是中斷的
舉個例子,當我需要刪除列表某一行的時候,我需要點擊刪除,彈出確認框,點擊確認之後ajax請求,刷新列表
在redux的流程裡面就會被割裂為兩條線:
1)點擊刪除-&>action -&> reducer -&> 改變彈窗狀態,此流程結束
2)點擊彈窗確認 -&> action -&> reducer -&> 更新列表,隱藏彈窗
實際類似行為是一個父-&>子-&>父這種控制權轉讓的場景,在redux下需要割裂為兩條線才能完成,且每一條線都需要自己去走一遍redux的action -&> reducer -&> state change這條線,新人理解起來極其繁瑣
2.大多數情況下,你的應用中state的生命周期與view的生命周期是完全一致的,在redux下,我需要額外的去自己同步兩者生命周期,會帶來編程上額外的注意事項。
3.redux自身不解決任何副作用分發問題,所以社區誕生了redux-thunk,redux-observable,redux-saga等各種middleware方案,看似生態無比繁榮,實則用起來各種蛋疼。
作為一個剛寫react幾個月的人的體會,redux的缺點就像是手動擋相較於自動擋的缺點。
理論上你可以更全面地掌控項目的每一個細節,但實際上你的操作量是比人家自動擋大很多的。老司機還好,萌新上來就用redux容易給自己繞進去。如果整個團隊里的編程口味不一樣,就會演變成一檔起步和二檔起步的老司機互相覺得對方的代碼是辣雞,旁邊的萌新在紅綠燈各種熄火。
對於小的項目來說太過複雜冗餘 不如用mobx或者直接props或者全局observer(逃 但是很龐大的項目尤其是需要多人協作的用redux這一套就很爽了 維護方便 干膩了也可以撒手就跑
缺點不知道,但是我知道有個優點 寫的代碼多
無法描述數據依賴關係有些ui天生就是響應式的,rxjs主場,複雜的話mobx也力不從心,redux寫到吐血
看了半天,第一反應。Durex需要編程??
推薦閱讀:
※react配合redux的生命周期(shouldComponentUpdate)的問題?
※揭秘Redux(1): 自動機
※redux 中的 state 樹太大會不會有性能問題?
TAG:Redux |