首頁  >  文章  >  web前端  >  Redux和Mobx的選擇實例詳解

Redux和Mobx的選擇實例詳解

小云云
小云云原創
2018-02-09 13:17:281446瀏覽

Redux 和 Mobx 都是當下比較火熱的資料流模型,似乎現在社群裡關於該選什麼來取代 Redux 很自然地成為了一件困惑的事。開發者不確定該選擇哪種解決方案。這個問題不只是出現在 Redux 與 Mobx 上。無論何時,只要有選擇,人們就會好奇最好的解決問題的方式是什麼。我現在寫的這些是為了解決 Redux 和 Mobx 這兩個狀態管理庫之間的困惑。

大部分的文章都用 React 來介紹 Mobx 和 Redux 的用法。但在大部分情況下你都可以將 React 替換成 Angular 、 Vue 或其他。

在 2016 年年初的時候我用 React + Redux 寫了一個相當大的應用程式。當我發現可以使用 Mobx 替代 Redux 時,我花時間將應用程式從 Redux 重構成了 Mobx 。現在我可以非常自在的使用它兩個並且解釋它倆的用法。

這篇文章會講什麼呢?如果你不打算看這麼長的文章(TLDR:too long, didn't read(查看此連結請自備梯子)),你可以看下目錄。但我想給你更多細節:第一,我想簡單回顧一下狀態管理函式庫為我們解決了什麼問題。畢竟我們寫 React 時只用 setState() 或寫其他 SPA 框架時用 setState() 類似的方法一樣也可以做的不錯。第二,我會大致的說下它們之間的相同之處和不同之處。第三,我會為 React 生態初學者指明怎樣學習 React 的狀態管理。友情提醒:在你深入 Mobx 和 Redux 之前,先使用 setState() 。最後,如果你已經有一個使用了 Mobx 或 Redux 的應用,我將會就如何從其中一個狀態管理函式庫重構到另一個給你更多我的理解。

目錄

我們要解決的是什麼問題?

Mobx 和 Redux 的不同?

React 狀態管理的學習曲線

嘗試另一個狀態管理方案?

最後思考

我們要解決的是什麼問題?

所有人都想在應用程式中使用狀態管理。但它為我們解決了什麼問題?很多人開始一個小應用程式時就已經引入一個狀態管理函式庫。大家都在談論 Mobx 和 Redux ,不是嗎?但大部分應用在一開始的時候並不需要大型的狀態管理。這甚至是危險的,因為這部分人將無法體驗 Mobx 和 Redux 這些庫所要解決的問題。

如今的現狀是要用元件(components)來建構一個前端應用。組件有自己的內部狀態。舉個栗子,在 React 中上述的本地狀態是用this.state和setState()來處理。但本地狀態的狀態管理在膨脹的應用中很快會變得混亂,因為:

一個元件需要和另一個元件共享狀態

一個元件需要當改變另一個元件的狀態

到某個程度時,推算應用的狀態將會變得越來越困難。它就會變成一個有很多狀態物件並且在元件層級上互相修改狀態的混亂應用。在大部分情況下,狀態物件和狀態的修改並沒有必要綁定在一些元件上。當你把狀態提升時,它們可以透過組件樹來得到。

所以,解決方案是引入狀態管理庫,例如:Mobx 或 Redux。它提供工具在某個地方保存狀態、修改狀態和更新狀態。你可以從一個地方獲得狀態,一個地方修改它,一個地方得到它的更新。它遵循單一資料來源的原則。這讓我們更容易推斷狀態的值和狀態的修改,因為它們與我們的元件是解耦的。

像是 Redux 和 Mobx 這類狀態管理函式庫一般都有附帶的工具,例如在 React 中使用的有 react-redux 和 mobx-react,它們使你的元件能夠獲得狀態。一般情況下,這些元件被叫做容器元件(container components),或者說的更確切的話,就是連接元件( connected components )。只要你將組件升級成連接組件,你就可以在組件層級的任何地方得到並更改狀態。

Mobx 和 Redux 的不同?

在我們深入了解 Redux 和 Mobx 的不同之前,我想先談談它們之間的相同之處。

這兩個函式庫都是用來管理 JavaScript 應用程式的狀態。它們不一定要跟 React 綁在一起,它們也可以在 AngularJs 和 VueJs 這些其他函式庫裡使用。但它們與 React 的理念結合得非常好。

如果你選擇了其中一個狀態管理方案,你不會覺得被它鎖定了。因為你可以在任何時候切換到另一個解決方案。你可以從 Mobx 換成 Redux 或從 Redux 換成 Mobx。我下面會展示如何能夠做到。

Dan Abramov 的 Redux 是從 flux 架構衍生出來的。和flux 不同的是,Redux 用單一store 而不是多個store 來保存state,另外,它用純函數替代dispatcher 來修改state,如果你對flux 不熟悉並且沒接觸過狀態管理,不要被這段內容所煩惱。

Redux 被 FP(函數式程式設計)原則所影響。 FP 可以在 JavaScript 中使用,但很多人有物件導向語言的背景,例如 Java。他們在剛開始的時候很難適應函數式程式的原則。這就是為什麼對於初學者來說 Mobx 可能更加簡單。

既然 Redux 擁抱 FP,那它使用的就是純函數。一個接受輸入並返回輸出並且沒有其他依賴的純函數。一個純函數在相同的輸入下輸出總是相同且沒有任何副作用。


(state, action) => newState

你的 Redux state 是不可變的,你應該總是回傳一個新的 state 而不是修改原始 state。你不應該執行 state 的修改或依據物件所引用的變更。


// 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 的習慣用法裡,state 的格式是像資料庫一樣標準化的。實體之間只靠 id 互相引用,這是最佳實踐。雖然不是每個人都這樣做,你也可以使用 normalizr 來使 state 標準化。標準化的 state 讓你能夠保持一個扁平的 state 和保持實體為單一資料來源。


{
 post: {
 id: 'a',
 authorId: 'b',
 ...
 },
 author: {
 id: 'b',
 postIds: ['a', ...],
 ...
 }
}

Michel Weststrate 的 Mobx 則是受到物件導向程式設計和響應式程式設計的影響。它將 state 包裝成可觀察的對象,因此你的 state 就有了 Observable 的所有能力。 state 資料可以只有普通的 setter 和 getter,但 observable 讓我們能在資料改變的時候得到更新的值。

Mobx 的state 是可變的,所以你直接的修改state :


function addAuthor(author) {
 this.authors.push(author);
}

除此之外,state 實體保持嵌套的資料結構來互相關聯。你不必標準化 state,而是讓它們保持嵌套。


{
 post: {
 id: 'a',
 ...
 author: {
 id: 'b',
 ...
 }
 }
}

單一store 與多stores

#在Redux 中,你將所有的state 放在一個全域的store。這個 store 物件就是你的單一資料來源。另一方面,多個 reducers 允許你修改不可變的 state。

Mobx 則相反,它使用多 stores。和 Redux 的 reducers 類似,你可以在技術層面或領域進行分治。也許你想在不同的 store 裡保存你的領域實體,但仍然保持對視圖中 state 的控制。畢竟你配置 state 是為了讓應用程式看起來更合理。

從技術層面來說,你一樣可以在 Redux 中使用多個 stores。沒有人強迫你只能只用一個 store。 但那不是 Redux 建議的用法。因為那違反了最佳實踐。在 Redux 中,你的單一 store 透過 reducers 的全域事件來回應更新。

如何使用?

你需要跟著下面的程式碼學習使用 Redux,先在全域 state 上新增一個 user 陣列。你可以看到我透過物件擴充運算子來傳回一個新物件。你同樣可以在 ES6(原文為 ES5,實際上是應該是 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'
 }
 ];
}

現在我們就可以呼叫 store 實例的方法:userStore.users.push(user);。這是一個最佳實踐,雖然使用 actions 去操作 state 的修改更清楚明確。


class UserStore {
 @observable users = [
 {
 name: 'Dan'
 },
 {
 name: 'Michel'
 }
 ];
 @action addUser = (user) => {
 this.users.push(user);
 }
}

在 Mobx 中你可以加上 useStrict() 來強制使用 action。現在你可以呼叫 store 實例上的方法:userStore.addUser(user); 來修改你的 state 。

你已經看到如何在 Redux 和 Mobx 中更新 state 。它們是不同的,Redux 中state 是唯讀的,你只能使用明確的actions 來修改state ,Mobx 則相反,state 是可讀和寫的,你可以不使用actions 直接修改state,但你可以useStrict( ) 來使用明確的actions 。

React 狀態管理的學習曲線

#React 應用廣泛使用 Redux 和 Mobx 。但它們是獨立的狀態管理函式庫,可以運用在 React 的任何地方。它們的互通函式庫讓我們能簡單的連接React 元件。 Redux + React 的 react-redux 和 MobX + React 的mobx-react 。稍後我會說明它倆如何在 React 元件樹中使用。

在最近的討論中,人們在爭論 Redux 的學習曲線。這通常發生在下面的情境:想使用 Redux 做狀態管理的 React 初學者。大部分人認為 React 和 Redux 本身都有相當高的學習曲線,兩者結合的話會失控。一個替代的選擇是 Mobx ,因為它更適合初學者。

然而,我会建议 React 的初学者一个学习状态管理的新方法。先学习React 组件内部的状态管理功能。在 React 应用,你首先会学到生命周期方法,而且你会用 setState() 和 this.state 解决本地的状态管理。我非常推荐上面的学习路径。不然你会在 React 的生态中迷失。在这条学习路径的最后,你会认识到组件内部管理状态难度越来越大。毕竟那是 The Road to learn React 书里如何教授 React 状态管理的方法。

现在我们重点讨论 Redux 和 Mobx 为我们解决了什么问题?它俩都提供了在组件外部管理应用状态的方法。state 与组件相互解耦,组件可以读取 state ,修改 state ,有新 state 时更新。这个 state 是单一数据源。

现在你需要选择其中一个状态管理库。这肯定是要第一时间解决的问题。此外,在开发过相当大的应用之后,你应该能很自如使用 React 。

初学者用 Redux 还是 Mobx ?

一旦你对 React 组件和它内部的状态管理熟悉了,你就能选择出一个状态管理库来解决你的问题。在我两个库都用过后,我想说 Mobx 更适合初学者。我们刚才已经看到 Mobx 只要更少的代码,甚至它可以用一些我们现在还不知道的魔法注解。
用 Mobx 你不需要熟悉函数式编程。像“不可变”之类的术语对你可能依然陌生。函数式编程是不断上升的范式,但对于大部分 JavaScript 开发者来说是新奇的。虽然它有清晰的趋势,但并非所有人都有函数式编程的背景,有面向对象背景的开发者可能会更加容易适应 Mobx 的原则。

   注:Mobx 可以很好的在 React 内部组件状态管理中代替 setState,我还是建议继续使用 setState() 管理内部状态。但链接文章很清楚的说明了在 React 中用 Mobx 完成内部状态管理是很容易的。                                                                      

规模持续增长的应用

在 Mobx 中你改变注解过的对象,组件就会更新。Mobx 比 Redux 使用了更多的内部魔法实现,因此在刚开始的时候只要更少的代码。有 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 更加明确。上面也提到过,有个小功能可以强制的使用 actions 修改 state 。


// root file
import { useStrict } from &#39;mobx&#39;;
useStrict(true);

这样的话第一个例子中直接修改 store 中的 state 就不再起作用了。前面的例子展示了怎样拥抱 Mobx 的最佳实践。此外,一旦你只用 actions ,你就已经使用了 Redux 的约束。

在快速启动一个项目时,我会推荐使用 Mobx ,一旦应用开始变得越来越大,越来越多的人开发时,遵循最佳实践就很有意义,如使用明确的 actions 。这是拥抱 Redux 的约束:你永远不能直接修改 state ,只能使用 actions 。

迁移到 Redux

一旦应用开始变得越来越大,越来越多的人开发时,你应该考虑使用 Redux 。它本身强制使用明确的 actions 修改 state 。action 有 type 和 payload 参数,reducer 可以用来修改 state 。这样的话,一个团队里的开发人员可以很简单的推断 state 的修改。


// reducer
(state, action) => newState

Redux 提供状态管理的整个架构,并有清晰的约束规则。这是 Redux 的成功故事。

另一个 Redux 的优势是在服务端使用。因为我们使用的是纯 JavaScript ,它可以在网络上传输 state 。序列化和反序列化一个 state 对象是直接可用的。当然 Mobx 也是一样可以的。

Mobx 是无主张的,但你可以通过 useStrict() 像 Redux 一样使用清晰的约束规则。这就是我为什么没说你不能在扩张的应用中使用 Mobx ,但 Redux 是有明确的使用方式的。而 Mobx 甚至在文档中说:“ Mobx 不会告诉你如何组织代码,哪里该存储 state 或 怎么处理事件。”所以开发团队首先要确定 state 的管理架构。

状态管理的学习曲线并不是很陡峭。我们总结下建议:React 初学者首先学习恰当的使用 setState() 和 this.state 。一段时间之后你将会意识到在 React 应用中仅仅使用 setState() 管理状态的问题。当你寻找解决方案时,你会在状态管理库 Mobx 或 Redux 的选择上犹豫。应该选哪个呢?由于 Mobx 是无主张的,使用上可以和 setState() 类似,我建议在小项目中尝试。一旦应用开始变得越来越大,越来越多的人开发时,你应该考虑在 Mobx 上实行更多的限制条件或尝试使用 Redux 。我使用两个库都很享受。即使你最后两个都没使用,了解到状态管理的另一种方式也是有意义的。

尝试另一个状态管理方案?

你可能已经使用了其中一个状态管理方案,但是想考虑另一个?你可以比较现实中的 Mobx 和 Redux 应用。我把所有的文件修改都提交到了一个Pull Request 。在这个 PR 里,项目从 Redux 重构成了 Mobx ,反之亦然,你可以自己实现。我不认为有必要和 Redux 或 Mobx 耦合,因为大部分的改变是和其他任何东西解耦的。

你主要需要将 Redux 的 Actions、Action Creator、 Action Types、Reducer、Global Store 替换成 Mobx 的 Stores 。另外将和 React 组件连接的接口 react-redux 换成 mobx-react 。presenter + container pattern 依然可以执行。你仅仅还要重构容器组件。在 Mobx 中可以使用 inject 获得 store 依赖。然后 store 可以传递 substate 和 actions 给组件。Mobx 的 observer 确保组件在 store 中 observable 的属性变化时更新。


import { observer, inject } from &#39;mobx-react&#39;;
...
const UserProfileContainer = inject(
 &#39;userStore&#39;
)(observer(({
 id,
 userStore,
}) => {
 return (
 <UserProfile
 user={userStore.getUser(id)}
 onUpdateUser={userStore.updateUser}
 />
 );
}));

Redux 的话,你使用 mapStateToProps 和 mapDispatchToProps 传递 substate 和 actions 给组件。


import { connect } from &#39;react-redux&#39;;
import { bindActionCreators } from &#39;redux&#39;;
...
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 重构为 Mobx指南。但就像我上面说过的,反过来一样也是可以的。一旦你选择了一个状态管理库,你会知道那并没有什么限制。它们基本上是和你的应用解耦的,所以是可以替换的。

最后思考

每当我看 Redux vs Mobx 争论下的评论时,总会有下面这条:“Redux 有太多的样板代码,你应该使用 Mobx,可以减少 xxx 行代码”。这条评论也许是对的,但没人考虑得失,Redux 比 Mobx 更多的样板代码,是因为特定的设计约束。它允许你推断应用状态即使应用规模很大。所以围绕 state 的仪式都是有原因的。

Redux 库非常小,大部分时间你都是在处理纯 JavaScript 对象和数组。它比 Mobx 更接近 vanilla JavaScript 。Mobx 通过包装对象和数组为可观察对象,从而隐藏了大部分的样板代码。它是建立在隐藏抽象之上的。感觉像是出现了魔法,但却很难理解其内在的机制。Redux 则可以简单通过纯 JavaScript 来推断。它使你的应用更简单的测试和调试。
另外,我们重新回到单页应用的最开始来考虑,一系列的单页应用框架和库面临着相同的状态管理问题,它最终被 flux 模式解决了。Redux 是这个模式的成功者。

Mobx 则又处在相反的方向。我们直接修改 state 而没有拥抱函数式编程的好处。对一些开发者来说,这让他们觉得像双向绑定。一段时间之后,由于没有引入类似 Redux 的状态管理库,他们可能又会陷入同样的问题。状态管理分散在各个组件,导致最后一团糟。

使用 Redux,你有一个既定的模式组织代码,而 Mobx 则无主张。但拥抱 Mobx 最佳实践会是明智的。 开发者需要知道如何组织状态管理从而更好的推断它。不然他们就会想要直接在组件中修改它。

两个库都非常棒。Redux 已经非常完善,Mobx 则逐渐成为一个有效的替代。

相关推荐:

react-redux中connect的装饰器用法

如何理解 redux

在React中使用Redux的实例详解

以上是Redux和Mobx的選擇實例詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn