我在去年大量的使用了 Redux,但我最近都在使用 Mobx 來做狀態(tài)(state)管理。似乎現(xiàn)在社區(qū)里關(guān)于該選什么來替代 Redux 很自然地成為了一件困惑的事。開發(fā)者不確定該選擇哪種解決方案。這個問題并不只是出現(xiàn)在 Redux 與 Mobx 上。無論何時,只要存在選擇,人們就會好奇最好的解決問題的方式是什么。我現(xiàn)在寫的這些是為了解決 Redux 和 Mobx 這兩個狀態(tài)管理庫之間的困惑。
成都創(chuàng)新互聯(lián)服務(wù)項(xiàng)目包括豐臺網(wǎng)站建設(shè)、豐臺網(wǎng)站制作、豐臺網(wǎng)頁制作以及豐臺網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,豐臺網(wǎng)站推廣取得了明顯的社會效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到豐臺省份的部分城市,未來相信會繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
大部分的文章都用 React 來介紹 Mobx 和 Redux 的用法。但是在大部分情況下你都可以將 React 替換成 Angular 、 Vue 或其他。
在 2016 年年初的時候我用 React + Redux 寫了一個相當(dāng)大的應(yīng)用。在我發(fā)現(xiàn)可以使用 Mobx 替代 Redux 時,我花時間將應(yīng)用從 Redux 重構(gòu)成了 Mobx 。現(xiàn)在我可以非常自在的使用它倆并且解釋它倆的用法。
這篇文章將要講什么呢?如果你不打算看這么長的文章(TLDR:too long, didn't read(查看此鏈接請自備梯子)),你可以看下目錄。但我想給你更多細(xì)節(jié):第一,我想簡單地回顧狀態(tài)管理庫為我們解決了什么問題。畢竟我們寫 React 時只用 setState() 或?qū)懫渌?SPA 框架時用 setState() 類似的方法一樣也可以做的不錯。第二,我會大致的說下它們之間的相同之處和不同之處。第三,我會給 React 生態(tài)初學(xué)者指明怎樣學(xué)習(xí) React 的狀態(tài)管理。友情提醒:在你深入 Mobx 和 Redux 之前,請先使用 setState() 。最后,如果你已經(jīng)有一個使用了 Mobx 或 Redux 的應(yīng)用,我將會就如何從其中一個狀態(tài)管理庫重構(gòu)到另一個給你更多我的理解。
目錄
我們要解決的是什么問題?
Mobx 和 Redux 的不同?
React 狀態(tài)管理的學(xué)習(xí)曲線
嘗試另一個狀態(tài)管理方案?
最后思考
我們要解決的是什么問題?
所有人都想在應(yīng)用中使用狀態(tài)管理。但它為我們解決了什么問題?很多人開始一個小應(yīng)用時就已經(jīng)引入一個狀態(tài)管理庫。所有人都在談?wù)?Mobx 和 Redux ,不是嗎?但大部分應(yīng)用在一開始的時候并不需要大型的狀態(tài)管理。這甚至是危險的,因?yàn)檫@部分人將無法體驗(yàn) Mobx 和 Redux 這些庫所要解決的問題。
如今的現(xiàn)狀是要用組件(components)來構(gòu)建一個前端應(yīng)用。組件有自己的內(nèi)部狀態(tài)。舉個栗子,在 React 中上述的本地狀態(tài)是用this.state和setState()來處理。但本地狀態(tài)的狀態(tài)管理在膨脹的應(yīng)用中很快會變得混亂,因?yàn)椋?/p>
一個組件需要和另一個組件共享狀態(tài)
一個組件需要改變另一個組件的狀態(tài)
到一定程度時,推算應(yīng)用的狀態(tài)將會變得越來越困難。它就會變成一個有很多狀態(tài)對象并且在組件層級上互相修改狀態(tài)的混亂應(yīng)用。在大部分情況下,狀態(tài)對象和狀態(tài)的修改并沒有必要綁定在一些組件上。當(dāng)你把狀態(tài)提升時,它們可以通過組件樹得到。
所以,解決方案是引入狀態(tài)管理庫,比如:Mobx 或 Redux。它提供工具在某個地方保存狀態(tài)、修改狀態(tài)和更新狀態(tài)。你可以從一個地方獲得狀態(tài),一個地方修改它,一個地方得到它的更新。它遵循單一數(shù)據(jù)源的原則。這讓我們更容易推斷狀態(tài)的值和狀態(tài)的修改,因?yàn)樗鼈兣c我們的組件是解耦的。
像 Redux 和 Mobx 這類狀態(tài)管理庫一般都有附帶的工具,例如在 React 中使用的有 react-redux 和 mobx-react,它們使你的組件能夠獲得狀態(tài)。一般情況下,這些組件被叫做容器組件(container components),或者說的更加確切的話,就是連接組件( connected components )。只要你將組件升級成連接組件,你就可以在組件層級的任何地方得到和更改狀態(tài)。
Mobx 和 Redux 的不同?
在我們深入了解 Redux 和 Mobx 的不同之前,我想先談?wù)勊鼈冎g的相同之處。
這兩個庫都是用來管理 JavaScript 應(yīng)用的狀態(tài)。它們并不一定要跟 React 綁定在一起,它們也可以在 AngularJs 和 VueJs 這些其他庫里使用。但它們與 React 的理念結(jié)合得非常好。
如果你選擇了其中一個狀態(tài)管理方案,你不會感到被它鎖定了。因?yàn)槟憧梢栽谌魏螘r候切換到另一個解決方案。你可以從 Mobx 換成 Redux 或從 Redux 換成 Mobx。我下面會展示如何能夠做到。
Dan Abramov 的 Redux 是從 flux 架構(gòu)派生出來的。和 flux 不同的是,Redux 用單一 store 而不是多個 store 來保存 state,另外,它用純函數(shù)替代 dispatcher 來修改 state,如果你對 flux 不熟并且沒接觸過狀態(tài)管理,不要被這段內(nèi)容所煩惱。
Redux 被 FP(函數(shù)式編程)原則所影響。FP 可以在 JavaScript 中使用,但很多人有面向?qū)ο笳Z言的背景,比如 Java。他們在剛開始的時候很難適應(yīng)函數(shù)式編程的原則。這就是為什么對于初學(xué)者來說 Mobx 可能更加簡單。
既然 Redux 擁抱 FP,那它使用的就是純函數(shù)。一個接受輸入并返回輸出并且沒有其他依賴的純函數(shù)。一個純函數(shù)在相同的輸入下輸出總是相同而且沒有任何副作用。
(state, action) => newState
你的 Redux state 是不可變的,你應(yīng)該總是返回一個新的 state 而不是修改原 state。你不應(yīng)該執(zhí)行 state 的修改或依據(jù)對象引用的更改。
// don't do this in Redux, because it mutates the array function addAuthor(state, action) { return state.authors.push(action.author); } // stay immutable and always return a new object function addAuthor(state, action) { return [ ...state.authors, action.author ]; }
最后,在 Redux 的習(xí)慣用法里,state 的格式是像數(shù)據(jù)庫一樣標(biāo)準(zhǔn)化的。實(shí)體之間只靠 id 互相引用,這是最佳實(shí)踐。雖然不是每個人都這樣做,你也可以使用 normalizr 來使 state 標(biāo)準(zhǔn)化。標(biāo)準(zhǔn)化的 state 讓你能夠保持一個扁平的 state 和保持實(shí)體為單一數(shù)據(jù)源。
{ post: { id: 'a', authorId: 'b', ... }, author: { id: 'b', postIds: ['a', ...], ... } }
Michel Weststrate 的 Mobx 則是受到面向?qū)ο缶幊毯晚憫?yīng)式編程的影響。它將 state 包裝成可觀察的對象,因此你的 state 就有了 Observable 的所有能力。state 數(shù)據(jù)可以只有普通的 setter 和 getter,但 observable 讓我們能在數(shù)據(jù)改變的時候得到更新的值。
Mobx 的 state 是可變的,所以你直接的修改 state :
function addAuthor(author) { this.authors.push(author); }
除此之外,state 實(shí)體保持嵌套的數(shù)據(jù)結(jié)構(gòu)來互相關(guān)聯(lián)。你不必標(biāo)準(zhǔn)化 state,而是讓它們保持嵌套。
{ post: { id: 'a', ... author: { id: 'b', ... } } }
單 store 與多 stores
在 Redux 中,你將所有的 state 都放在一個全局的 store。這個 store 對象就是你的單一數(shù)據(jù)源。另一方面,多個 reducers 允許你修改不可變的 state。
Mobx 則相反,它使用多 stores。和 Redux 的 reducers 類似,你可以在技術(shù)層面或領(lǐng)域進(jìn)行分治。也許你想在不同的 stores 里保存你的領(lǐng)域?qū)嶓w,但仍然保持對視圖中 state 的控制。畢竟你配置 state 是為了讓應(yīng)用看起來更合理。
從技術(shù)層面來說,你一樣可以在 Redux 中使用多個 stores。沒有人強(qiáng)迫你只能只用一個 store。 但那不是 Redux 建議的用法。因?yàn)槟沁`反了最佳實(shí)踐。在 Redux 中,你的單 store 通過 reducers 的全局事件來響應(yīng)更新。
如何使用?
你需要跟隨下面的代碼學(xué)習(xí)使用 Redux,首先在全局 state 上新增一個 user 數(shù)組。你可以看到我通過對象擴(kuò)展運(yùn)算符來返回一個新對象。你同樣可以在 ES6(原文為 ES5,實(shí)際是應(yīng)該是 ES6)中使用 Object.assign() 來操作不可變對象。
const initialState = { users: [ { name: 'Dan' }, { name: 'Michel' } ] }; // reducer function users(state = initialState, action) { switch (action.type) { case 'USER_ADD': return { ...state, users: [ ...state.users, action.user ] }; default: return state; } } // action { type: 'USER_ADD', user: user };
你必須使用 dispatch({ type: 'USER_ADD', user: user });來為全局 state 添加一個新 user 。
在 Mobx 中,一個 store 只管理一個子 state(就像 Redux 中管理子 state 的 reducer),但你可以直接修改 state 。
@observable 讓我們可以觀察到 state 的變化。
class UserStore { @observable users = [ { name: 'Dan' }, { name: 'Michel' } ]; }
現(xiàn)在我們就可以調(diào)用 store 實(shí)例的方法:userStore.users.push(user);。這是一種最佳實(shí)踐,雖然使用 actions 去操作 state 的修改更加清楚明確。
class UserStore { @observable users = [ { name: 'Dan' }, { name: 'Michel' } ]; @action addUser = (user) => { this.users.push(user); } }
在 Mobx 中你可以加上 useStrict() 來強(qiáng)制使用 action。現(xiàn)在你可以調(diào)用 store 實(shí)例上的方法:userStore.addUser(user); 來修改你的 state 。
你已經(jīng)看到如何在 Redux 和 Mobx 中更新 state 。它們是不同的,Redux 中 state 是只讀的,你只能使用明確的 actions 來修改 state ,Mobx 則相反,state 是可讀和寫的,你可以不使用 actions 直接修改 state,但你可以 useStrict() 來使用明確的 actions 。
React 狀態(tài)管理的學(xué)習(xí)曲線
React 應(yīng)用廣泛使用 Redux 和 Mobx 。但它們是獨(dú)立的狀態(tài)管理庫,可以運(yùn)用在除 React 的任何地方。它們的互操作庫讓我們能簡單的連接React 組件。Redux + React 的 react-redux 和 MobX + React 的mobx-react 。稍后我會說明它倆如何在 React 組件樹中使用。
在最近的討論中,人們在爭論 Redux 的學(xué)習(xí)曲線。這通常發(fā)生在下面的情境中:想使用 Redux 做狀態(tài)管理的 React 初學(xué)者。大部分人認(rèn)為 React 和 Redux 本身都有頗高的學(xué)習(xí)曲線,兩者結(jié)合的話會失控。一個替代的選擇就是 Mobx ,因?yàn)樗m合初學(xué)者。
然而,我會建議 React 的初學(xué)者一個學(xué)習(xí)狀態(tài)管理的新方法。先學(xué)習(xí)React 組件內(nèi)部的狀態(tài)管理功能。在 React 應(yīng)用,你首先會學(xué)到生命周期方法,而且你會用 setState() 和 this.state 解決本地的狀態(tài)管理。我非常推薦上面的學(xué)習(xí)路徑。不然你會在 React 的生態(tài)中迷失。在這條學(xué)習(xí)路徑的最后,你會認(rèn)識到組件內(nèi)部管理狀態(tài)難度越來越大。畢竟那是 The Road to learn React 書里如何教授 React 狀態(tài)管理的方法。
現(xiàn)在我們重點(diǎn)討論 Redux 和 Mobx 為我們解決了什么問題?它倆都提供了在組件外部管理應(yīng)用狀態(tài)的方法。state 與組件相互解耦,組件可以讀取 state ,修改 state ,有新 state 時更新。這個 state 是單一數(shù)據(jù)源。
現(xiàn)在你需要選擇其中一個狀態(tài)管理庫。這肯定是要第一時間解決的問題。此外,在開發(fā)過相當(dāng)大的應(yīng)用之后,你應(yīng)該能很自如使用 React 。
初學(xué)者用 Redux 還是 Mobx ?
一旦你對 React 組件和它內(nèi)部的狀態(tài)管理熟悉了,你就能選擇出一個狀態(tài)管理庫來解決你的問題。在我兩個庫都用過后,我想說 Mobx 更適合初學(xué)者。我們剛才已經(jīng)看到 Mobx 只要更少的代碼,甚至它可以用一些我們現(xiàn)在還不知道的魔法注解。
用 Mobx 你不需要熟悉函數(shù)式編程。像“不可變”之類的術(shù)語對你可能依然陌生。函數(shù)式編程是不斷上升的范式,但對于大部分 JavaScript 開發(fā)者來說是新奇的。雖然它有清晰的趨勢,但并非所有人都有函數(shù)式編程的背景,有面向?qū)ο蟊尘暗拈_發(fā)者可能會更加容易適應(yīng) Mobx 的原則。
注:Mobx 可以很好的在 React 內(nèi)部組件狀態(tài)管理中代替 setState,我還是建議繼續(xù)使用 setState() 管理內(nèi)部狀態(tài)。但鏈接文章很清楚的說明了在 React 中用 Mobx 完成內(nèi)部狀態(tài)管理是很容易的。
規(guī)模持續(xù)增長的應(yīng)用
在 Mobx 中你改變注解過的對象,組件就會更新。Mobx 比 Redux 使用了更多的內(nèi)部魔法實(shí)現(xiàn),因此在剛開始的時候只要更少的代碼。有 Angular 背景的會覺得跟雙向綁定很像。你在一個地方保存 state ,通過注解觀察 state ,一旦 state 修改組件會自動的更新。
Mobx 允許直接在組件樹上直接修改 state 。
// component <button onClick={() => store.users.push(user)} />
更好的方式是用 store 的 @action 。
// component <button onClick={() => store.addUser(user)} /> // store @action addUser = (user) => { this.users.push(user); }
用 actions 修改 state 更加明確。上面也提到過,有個小功能可以強(qiáng)制的使用 actions 修改 state 。
// root file import { useStrict } from 'mobx'; useStrict(true);
這樣的話第一個例子中直接修改 store 中的 state 就不再起作用了。前面的例子展示了怎樣擁抱 Mobx 的最佳實(shí)踐。此外,一旦你只用 actions ,你就已經(jīng)使用了 Redux 的約束。
在快速啟動一個項(xiàng)目時,我會推薦使用 Mobx ,一旦應(yīng)用開始變得越來越大,越來越多的人開發(fā)時,遵循最佳實(shí)踐就很有意義,如使用明確的 actions 。這是擁抱 Redux 的約束:你永遠(yuǎn)不能直接修改 state ,只能使用 actions 。
遷移到 Redux
一旦應(yīng)用開始變得越來越大,越來越多的人開發(fā)時,你應(yīng)該考慮使用 Redux 。它本身強(qiáng)制使用明確的 actions 修改 state 。action 有 type 和 payload 參數(shù),reducer 可以用來修改 state 。這樣的話,一個團(tuán)隊里的開發(fā)人員可以很簡單的推斷 state 的修改。
// reducer (state, action) => newState
Redux 提供狀態(tài)管理的整個架構(gòu),并有清晰的約束規(guī)則。這是 Redux 的成功故事。
另一個 Redux 的優(yōu)勢是在服務(wù)端使用。因?yàn)槲覀兪褂玫氖羌?JavaScript ,它可以在網(wǎng)絡(luò)上傳輸 state 。序列化和反序列化一個 state 對象是直接可用的。當(dāng)然 Mobx 也是一樣可以的。
Mobx 是無主張的,但你可以通過 useStrict() 像 Redux 一樣使用清晰的約束規(guī)則。這就是我為什么沒說你不能在擴(kuò)張的應(yīng)用中使用 Mobx ,但 Redux 是有明確的使用方式的。而 Mobx 甚至在文檔中說:“ Mobx 不會告訴你如何組織代碼,哪里該存儲 state 或 怎么處理事件?!彼蚤_發(fā)團(tuán)隊首先要確定 state 的管理架構(gòu)。
狀態(tài)管理的學(xué)習(xí)曲線并不是很陡峭。我們總結(jié)下建議:React 初學(xué)者首先學(xué)習(xí)恰當(dāng)?shù)氖褂?setState() 和 this.state 。一段時間之后你將會意識到在 React 應(yīng)用中僅僅使用 setState() 管理狀態(tài)的問題。當(dāng)你尋找解決方案時,你會在狀態(tài)管理庫 Mobx 或 Redux 的選擇上猶豫。應(yīng)該選哪個呢?由于 Mobx 是無主張的,使用上可以和 setState() 類似,我建議在小項(xiàng)目中嘗試。一旦應(yīng)用開始變得越來越大,越來越多的人開發(fā)時,你應(yīng)該考慮在 Mobx 上實(shí)行更多的限制條件或嘗試使用 Redux 。我使用兩個庫都很享受。即使你最后兩個都沒使用,了解到狀態(tài)管理的另一種方式也是有意義的。
嘗試另一個狀態(tài)管理方案?
你可能已經(jīng)使用了其中一個狀態(tài)管理方案,但是想考慮另一個?你可以比較現(xiàn)實(shí)中的 Mobx 和 Redux 應(yīng)用。我把所有的文件修改都提交到了一個Pull Request 。在這個 PR 里,項(xiàng)目從 Redux 重構(gòu)成了 Mobx ,反之亦然,你可以自己實(shí)現(xiàn)。我不認(rèn)為有必要和 Redux 或 Mobx 耦合,因?yàn)榇蟛糠值母淖兪呛推渌魏螙|西解耦的。
你主要需要將 Redux 的 Actions、Action Creator、 Action Types、Reducer、Global Store 替換成 Mobx 的 Stores 。另外將和 React 組件連接的接口 react-redux 換成 mobx-react 。presenter + container pattern 依然可以執(zhí)行。你僅僅還要重構(gòu)容器組件。在 Mobx 中可以使用 inject 獲得 store 依賴。然后 store 可以傳遞 substate 和 actions 給組件。Mobx 的 observer 確保組件在 store 中 observable 的屬性變化時更新。
import { observer, inject } from 'mobx-react'; ... const UserProfileContainer = inject( 'userStore' )(observer(({ id, userStore, }) => { return ( <UserProfile user={userStore.getUser(id)} onUpdateUser={userStore.updateUser} /> ); }));
Redux 的話,你使用 mapStateToProps 和 mapDispatchToProps 傳遞 substate 和 actions 給組件。
import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; ... function mapStateToProps(state, props) { const { id } = props; const user = state.users[id]; return { user, }; } function mapDispatchToProps(dispatch) { return { onUpdateUser: bindActionCreators(actions.updateUser, dispatch), }; } const UserProfileContainer = connect(mapStateToProps, mapDispatchToProps)(UserProfile);
這有一篇怎樣將 Redux 重構(gòu)為 Mobx指南。但就像我上面說過的,反過來一樣也是可以的。一旦你選擇了一個狀態(tài)管理庫,你會知道那并沒有什么限制。它們基本上是和你的應(yīng)用解耦的,所以是可以替換的。
最后思考
每當(dāng)我看 Redux vs Mobx 爭論下的評論時,總會有下面這條:“Redux 有太多的樣板代碼,你應(yīng)該使用 Mobx,可以減少 xxx 行代碼”。這條評論也許是對的,但沒人考慮得失,Redux 比 Mobx 更多的樣板代碼,是因?yàn)樘囟ǖ脑O(shè)計約束。它允許你推斷應(yīng)用狀態(tài)即使應(yīng)用規(guī)模很大。所以圍繞 state 的儀式都是有原因的。
Redux 庫非常小,大部分時間你都是在處理純 JavaScript 對象和數(shù)組。它比 Mobx 更接近 vanilla JavaScript 。Mobx 通過包裝對象和數(shù)組為可觀察對象,從而隱藏了大部分的樣板代碼。它是建立在隱藏抽象之上的。感覺像是出現(xiàn)了魔法,但卻很難理解其內(nèi)在的機(jī)制。Redux 則可以簡單通過純 JavaScript 來推斷。它使你的應(yīng)用更簡單的測試和調(diào)試。
另外,我們重新回到單頁應(yīng)用的最開始來考慮,一系列的單頁應(yīng)用框架和庫面臨著相同的狀態(tài)管理問題,它最終被 flux 模式解決了。Redux 是這個模式的成功者。
Mobx 則又處在相反的方向。我們直接修改 state 而沒有擁抱函數(shù)式編程的好處。對一些開發(fā)者來說,這讓他們覺得像雙向綁定。一段時間之后,由于沒有引入類似 Redux 的狀態(tài)管理庫,他們可能又會陷入同樣的問題。狀態(tài)管理分散在各個組件,導(dǎo)致最后一團(tuán)糟。
使用 Redux,你有一個既定的模式組織代碼,而 Mobx 則無主張。但擁抱 Mobx 最佳實(shí)踐會是明智的。 開發(fā)者需要知道如何組織狀態(tài)管理從而更好的推斷它。不然他們就會想要直接在組件中修改它。
兩個庫都非常棒。Redux 已經(jīng)非常完善,Mobx 則逐漸成為一個有效的替代。
總結(jié)
以上就是本文關(guān)于在Redux 和 Mobx之間如何選擇的問題,相信大家閱讀過本文之后應(yīng)該有了清晰的了解。
謝謝大家對本站的支持!
新聞標(biāo)題:Redux和Mobx的選擇問題:讓你不再困惑!
網(wǎng)址分享:http://jinyejixie.com/article32/igogsc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站內(nèi)鏈、靜態(tài)網(wǎng)站、網(wǎng)站制作、域名注冊、網(wǎng)站策劃、面包屑導(dǎo)航
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)