摘要:需要注意的是,在中,需要把數據聲明為。同時還提供了運行時的類型安全檢查。在利用了,使異步操作可以在一個函數內完成并且可以被追蹤。例如在中,數組并不是一個,而是一個類的對象,這是為了能監聽到數據下標的賦值。
Redux是一個數據管理層,被廣泛用于管理復雜應用的數據。但是實際使用中,Redux的表現差強人意,可以說是不好用。而同時,社區也出現了一些數據管理的方案,Mobx就是其中之一。
Redux的問題Predictable state container for JavaScript apps
這是Redux給自己的定位,但是這其中存在很多問題。
首先,Redux做了什么?看Redux的源碼,createStore只有一個函數,返回4個閉包。dispatch只做了一件事,調用reducer然后調用subscribe的listener,這其中state的不可變或者是可變全部由使用者來控制,Redux并不知道state有沒有發生變化,更不知道state具體哪里發生了變化。所以,如果view層需要知道哪一部分需要更新,只能通過臟檢查。
再看react-redux做了什么,在store.subscribe上掛回調,每次發生subscribe就調用connect傳進去mapStateToProps和mapDispatchToProps,然后臟檢測props的每一項。當然,我們可以利用不可變數據的特點,去減少prop的數量從而減少臟檢測的次數,但是哪有props都來自同一個子樹這么好的事呢?
所以,如果有n個組件connect,每當dispatch一個action的時候,無論做了什么粒度的更新,都會發生O(n)時間復雜度的臟檢測。
// Redux 3.7.2 createStore.js // ... try { isDispatching = true currentState = currentReducer(currentState, action) } finally { isDispatching = false } const listeners = currentListeners = nextListeners for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } // ...
更糟糕的是,每次reducer執行完Redux就直接調用listener了,如果在短時間內發生了多次修改(例如用戶輸入),不可變的開銷,加上redux用字符串匹配action的開銷,臟檢測的開銷,再加上view層的開銷,整個性能表現會非常糟糕,即使在用戶輸入的時候往往只需要更新一個"input"。應用規模越大,性能表現越糟糕。(這里的應用指單個頁面。這里的單頁不是SPA的單頁的意思,因為有Router的情況下,被切走的頁面其所有組件都被unmount了)
在應用規模增大的同時,異步請求數量一多,Redux所宣傳的Predictable也根本就是泡影,更多的時候是配合各種工具淪為數據可視化工具。
MobxMobx可以說是眾多數據方案中最完善的一個了。Mobx本身獨立,不與任何view層框架互相依賴,因此你可以隨意選擇合適的view層框架(部分除外,例如Vue,因為它們的原理是一樣的)。
目前Mobx(3.x)和Vue(2.x)采用了相同的響應式原理,借用Vue文檔的一張圖:
為每個組件創建一個Watcher,在數據的getter和setter上加鉤子,當組件渲染的時候(例如,調用render方法)會觸發getter,然后把這個組件對應的Watcher添加到getter相關的數據的依賴中(例如,一個Set)。當setter被觸發時,就能知道數據發生了變化,然后同時對應的Watcher去重繪組件。
這樣,每個組件所需要的數據時精確可知的,因此當數據發生變化時,可以精確地知道哪些組件需要被重繪,數據變化時重繪的過程是O(1)的時間復雜度。
需要注意的是,在Mobx中,需要把數據聲明為observable。
import React from "react"; import ReactDOM from "react-dom"; import { observable, action } from "mobx"; import { Provider, observer, inject } from "mobx-react"; class CounterModel { @observable count = 0 @action increase = () => { this.count += 1; } } const counter = new CounterModel(); @inject("counter") @observer class App extends React.Component { render() { const { count, increase } = this.props.counter; return (性能{count}) } } ReactDOM.render();
在這篇文章中,作者使用了一個一個128*128的繪圖板來說明問題。
由于Mobx利用getter和setter(未來可能會出現一個平行的基于Proxy的版本)去收集組件實例的數據依賴關系,因此每單當一個點發生更新的時候,Mobx知道哪些組件需要被更新,決定哪個組件更新的過程的時間復雜度是O(1)的,而Redux通過臟檢查每一個connect的組件去得到哪些組件需要更新,有n個組件connect這個過程的時間復雜度就是O(n),最終反映到Perf工具上就是JavaScript的執行耗時。
雖然在經過一系列優化后,Redux的版本可以獲得不輸Mobx版本的性能,當時Mobx不用任何優化就可以得到不錯的性能。而Redux最完美的優化是為每一個點建立多帶帶的store,這與Mobx等一眾精確定位數據依賴的方案在思想上是相同的。
Mobx State TreeMobx并不完美。Mobx不要求數據在一顆樹上,因此對Mobx進行數據可是化或者是記錄每次的數據變化變得不太容易。在Mobx的基礎上,Mobx State Tree誕生了。同Redux一樣,Mobx State Tree要求數據在一顆樹上,這樣對數據進行可視化和追蹤就變得非常容易,對開發來說是福音。同時Mobx State Tree非常容易得到準確的TypeScript類型定義,這一點Redux不容易做到。同時還提供了運行時的類型安全檢查。
import React from "react"; import ReactDOM from "react-dom"; import { types } from "mobx-state-tree"; import { Provider, observer, inject } from "mobx-react"; const CountModel = types.model("CountModel", { count: types.number }).actions(self => ({ increase() { self.count += 1; } })); const store = CountModel.create({ count: 0 }); @inject(({ store }) => ({ count: store.count, increase: store.increase })) class App extends React.Component { render() { const { count, increase } = this.props; return ({count}) } } ReactDOM.render();
Mobx State Tree還提供了snapshot的功能,因此雖然MST本身的數據可變,依然能打到不可變的數據的效果。官方提供了利用snaptshot直接結合Redux的開發工具使用,方便開發;同時官方還提供了把MST的數據作為一個Redux的store來使用;當然,利用snapshot也可以MST嵌在Redux的store中作為數據(類似在Redux中很流行的Immutable.js的作用)。
// 連接Redux的開發工具 // ... connectReduxDevtools(require("remotedev"), store); // ... // 直接作為一個Redux store使用 // ... import { Provider, connect } from "react-redux"; const store = asReduxStore(store); @connect(// ...) function SomeComponent() { return Some Component } ReactDOM.render(, document.getElementById("foo") ); // ...
并且,在MST中,可變數據和不可變的數據(snapshot)可以互相轉化,你可以隨時把snapshot應用到數據上。
applySnapshot(counter, { count: 12345 });
除此之外,官方還提供了異步action的支持。由于JavaScript的限制,異步操作難以被追蹤,即時使用了async函數,其執行過程中也是不能被追蹤的,就會出現雖然在async的函數內操作了數據,這個async函數也被標記為action,但是會被誤判是在action外修改了數據。以往異步action只能通過多個action組合使用來完成,而Vue則是通過把action和mutation分開來實現。在Mobx State Tree利用了Generator,使異步操作可以在一個action函數內完成并且可以被追蹤。
// ... SomeModel.actions(self => ({ someAsyncAction: process(function* () { const a = 1; const b = yield foo(a); // foo必須返回一個Promise self.bar = b; }) })); // ...總結
Mobx利用getter和setter來收集組件的數據依賴關系,從而在數據發生變化的時候精確知道哪些組件需要重繪,在界面的規模變大的時候,往往會有很多細粒度更新,雖然響應式設計會有額外的開銷,在界面規模大的時候,這種開銷是遠比對每一個組件做臟檢查小的,因此在這種情況下Mobx會很容易得到比Redux更好的性能。而在數據全部發生改變時,基于臟檢查的實現會比Mobx這類響應式有更好的性能,但這類情況很少。同時,有些benchmark并不是最佳實踐,其結果也不能反映真實的情況。
但是,由于React本身提供了利用不可變數據結構來減少無用渲染的機制(例如PureComponent,函數式組件),同時,React的一些生態和Immutable綁定了(例如Draft.js),因此在配合可變的觀察者模式的數據結構時并不是那么舒服。所以,在遇到性能問題之前,建議還是使用Redux和Immutable.js搭配React。
一些實踐The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming.
由于JavaScript的限制,一些對象不是原生的對象,其他的類型檢查庫可能會導致意想不到的結果。例如在Mobx中,數組并不是一個Array,而是一個類Array的對象,這是為了能監聽到數據下標的賦值。相對的,在Vue中數組是一個Array,但是數組下標賦值要使用splice來進行,否則無法被檢測到。
由于Mobx的原理,要做到精確的按需更新,就要在正確的地方觸發getter,最簡單的辦法就是render要用到的數據只在render里解構。mobx-react從4.0開始,inject接受的map函數中的結構也會被追蹤,因此可以直接用類似react-redux的寫法。注意,在4.0之前inject的map函數不會被追蹤。
響應式有額外的開銷,這些開銷在渲染大量數據時會對性能有影響(例如:長列表),因此要合理搭配使用observable.ref、observable.shallow(Mobx),types.frozen(Mobx State Tree)。
本文首發于有贊技術博客。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/90088.html
摘要:我現在寫的這些是為了解決和這兩個狀態管理庫之間的困惑。這甚至是危險的,因為這部分人將無法體驗和這些庫所要解決的問題。這肯定是要第一時間解決的問題。函數式編程是不斷上升的范式,但對于大部分開發者來說是新奇的。規模持續增長的應 原文地址:Redux or MobX: An attempt to dissolve the Confusion 原文作者:rwieruch 我在去年大量的使用...
摘要:前端每周清單半年盤點之與篇前端每周清單專注前端領域內容,以對外文資料的搜集為主,幫助開發者了解一周前端熱點分為新聞熱點開發教程工程實踐深度閱讀開源項目巔峰人生等欄目。與求同存異近日,宣布將的構建工具由遷移到,引發了很多開發者的討論。 前端每周清單半年盤點之 React 與 ReactNative 篇 前端每周清單專注前端領域內容,以對外文資料的搜集為主,幫助開發者了解一周前端熱點;分為...
摘要:版本支持動態,對比九月支持的靜態,動態會返回請求模塊命名空間的對象以供使用使用開發瀏覽器插件的過程與收獲初次認識是在年阿里的論壇會上,只知道它是運行在天生支持跨平臺性的語言,好像很值得關注。 團隊分享 React 整潔代碼最佳實踐 作為開發人員不能僅僅滿足于代碼可以工作,而應該讓代碼更易于編寫,閱讀和維護,這篇文章介紹了很多 clean code 在 React 應用開發上的最佳實踐。...
摘要:要求通過要求數據變更函數使用裝飾或放在函數中,目的就是讓狀態的變更根據可預測性單向數據流。同一份數據需要響應到多個視圖,且被多個視圖進行變更需要維護全局狀態,并在他們變動時響應到視圖數據流變得復雜,組件本身已經無法駕馭。今天是 520,這是本系列最后一篇文章,主要涵蓋 React 狀態管理的相關方案。 前幾篇文章在掘金首發基本石沉大海, 沒什么閱讀量. 可能是文章篇幅太長了?掘金值太低了? ...
摘要:它是由一個非常聰明的人開發的,用來緩解在單頁面應用中管理狀態的問題。的問題沒有一種適合所有場景的完美工具。為設計的是世界的另一個新增內容,但目前僅適用于。這將導致最后期限延長,并且留下更多需要我們維護的代碼。 原文:The Problems with Redux: Can React, MobX, and Realm save us? 作者:Erich Reich 首先,我不討厭 ...
閱讀 3768·2023-04-25 21:09
閱讀 3128·2021-10-20 13:48
閱讀 2942·2021-09-24 10:25
閱讀 2931·2021-08-21 14:08
閱讀 1790·2019-08-30 15:56
閱讀 976·2019-08-30 15:52
閱讀 1841·2019-08-29 14:11
閱讀 3562·2019-08-29 11:01