日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Redux vs Mobx系列(-):immutable vs mutable

發(fā)布時間:2025/3/20 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Redux vs Mobx系列(-):immutable vs mutable 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

**注意:**我會寫多篇文章來比較說明redux和mobx的不同,redux和mobx各有優(yōu)缺點, 如果對React/Mobx/Redux都理解夠深刻,我個人推薦Mobx(逃跑。。。)

React社區(qū)的大方向是immutable, 不管是用immutable.js 還是函數(shù)式編程使用不可變數(shù)據(jù)結構。為什么React需要不可變數(shù)據(jù)結構呢? 考慮下面的一個應用

class Root extends Component {state = {something: 'sh'}render() {return (<div><div onClick={e => { // onClick setState 空對象this.setState({})}}>click me!!</div><L1/><Dog sh={this.state.something}/></div>)} }... class L1 extends Component {render() {console.log('invoke L1')return (<div><L11/><L12/></div>)} } ... class L122 extends Component {render() {console.log('invoke L122')return (<div>L122</div>)} } 復制代碼

當我點擊 Root上的 click me 的時候, 執(zhí)行了this.setState({}),于是觸發(fā)Root更新, 這個時候L1, Dog會怎么樣呢? 結論是當點擊的時候 控制臺會打印:

invoke L1 invoke L11 invoke L111 invoke L112 invoke L12 invoke L121 invoke L122 invoke Dog 復制代碼

當一個組件需要跟新的時候,react并不知道哪里會更新,在內部react會用object(存js對象)來代表dom結構, 當有更新的時候 react暴力比較前后object的差異,增量的處理更新的dom部分。 對于剛才的這個例子, react暴力計算的結果就是沒有增量。。。雖然react暴力比較算法已經(jīng)非常高效了,這些無意義的計算也應該避免, 起碼可以節(jié)省計算機的電 --> 少用煤 --> 減少二氧化碳排放 --> 保護地球。 畢竟 蝴蝶效應!

ui = f(d) 相同的d得到相同的ui(設計組件的時候最好這樣)。例如我們上例的Dog,我們可以直接比較sh

class Dog extends Component {shouldComponentUpdate(nextProps) {return this.props.sh !== nextProps.sh}... } 復制代碼

更加一般的情況, 我們怎么確定組件的props和state沒有變化呢? 不可變對象 ! 如果對象是不可變的, 那么當對象a !== a' 就代表這是2個對象,不相等。而在傳統(tǒng)可變的對象中 需要deepEqual(a, a')。 如果我們的React應用里面 props和state都是不可變對象, 那么:

class X extends Component {shouldComponentUpdate(nextProps, nextState) {return !( shallowEqual(this.props, nextProps) && shallowEqual(this.state, nextState))} } 復制代碼

react也考慮到了一點 提供了PureComponent幫助我們默認做了這個shouldComponentUpdate

把 L1, Dog, L11 ... L122改為PureComponent, 再次點擊,打印:

// 沒有輸出。。。 復制代碼

拯救了地球!

Redux

redux 每次action發(fā)生的時候,都會返回一個全新的state,�天生是immutable。 Redux + PureComponent 輕松開發(fā)出高效web應用

Mobx

Mobx剛好相反,它依賴副作用(so 所有組件不在繼承PureComponent), 那它是怎么工作的呢?

mobx-react的 @observer通過收集組件 render函數(shù)依賴的狀態(tài), 當狀態(tài)有修改的時候精確的控制組件的更新。

比如現(xiàn)在 Root組件依賴狀態(tài) title, L122 依賴狀態(tài)x(Root傳遞x給L1,L1傳遞給L12, L12傳遞給L122)。 那么應該:

const store = observable({x: 'x'title: 'title', })window.store = store @observer export default class MobxRoot extends Component {render() {console.log('invoke MobxRoot')const { title, x } = storereturn (<div><div>{title}</div><L1 x={x}/><Dog/></div>)} } class L1 extends Component {render() {console.log('invoke L1')return (<div><L11/><L12 x={this.props.x}/></div>)} } class L12 extends Component {render() {console.log('invoke L12')return (<div><L121/><L122 x={this.props.x}/></div>)} } @observer class L122 extends Component {render() {console.log('invoke L122')return (<div>{ this.props.x || 'L122'}</div>)} } 復制代碼

這樣當title變化的時候, Mobx發(fā)現(xiàn)只有MobxRoot組件關心title,于是更新MobxRoot, 當x變化的時候 Mobx發(fā)現(xiàn)有MobxRoot, L122 依賴與x,于是更新MobxRoot,L122 。 工作很正常。

細想當title變化的時候,更新MobxRoot,由于更新了MobxRoot進而導致L1,Dog的遞歸暴力diff計算,顯而易見的是無意義的計算。 當x變化的時候呢, 由于MobxRoot,L122依賴了x, 會先更新MobxRoot,然后更新L122,然而在更新MobxRoot的時候又會遞歸的更新到L122, 這里更加麻煩了(實際上React不會更新兩次L122)。

Mobx也在文檔里指出了這個問題(晚一點使用間接引用值), 對應的解決方法是 L1 先傳遞store。。。最后在L122里面從store里面獲取x。

這里暴露了兩個問題:

  • 父組件的更新,會影響到子組件,由于不是使用不可變數(shù)據(jù),還不能簡單的通過PureComponent優(yōu)化
  • props傳遞的過程中 不可避免的會提前使用引用值,導致某些組件無意義的更新, 狀態(tài)越多越復雜
  • 記住在mobx應用里, 應該把組件是否更新的絕對權完全交給Mobx,完全交給Mobx,完全交給Mobx。 即使是父組件也不應該引起子組件的跟新。 所以所有的組件(沒有被@observer修飾)都應該繼承與PureComponent(這里的PureComponent的作用已經(jīng)不是原來的了, 這里的作用是阻止更新行為的傳遞)。 另外一點, 由于組件是否更新取決與Mobx, 組件更新的數(shù)據(jù)又取值與Mobx,所以還有必要props傳遞嗎? 基于這兩點代碼:

    const store = observable({x: 'x'title: 'title', })window.store = store @observer export default class MobxRoot extends Component {render() {console.log('invoke MobxRoot')const { title} = storereturn (<div><div>{title}</div><L1/><Dog/></div>)} } class L1 extends PureComponent {render() {console.log('invoke L1')return (<div><L11/><L12/></div>)} } class L12 extends PureComponent {render() {console.log('invoke L12')return (<div><L121/><L122/></div>)} } @observer class L122 extends Component {render() {console.log('invoke L122')const x = window.store // 直接從Mobx獲取return (<div>{ x || 'L122'}</div>)} } 復制代碼

    這樣當title改變的時候, 只有MobxRoot會跟新, 當x改變的時候只有L122 會更新。 現(xiàn)在我們可以把應用里面的所有組件分為兩類: 關注狀態(tài)的@observer組件, 其他PureComponent組件。這樣每當有狀態(tài)改變的時候, Mobx精確控制需要更新的@observer組件(最小的更新集合),其他PureComponent阻止無意義的更新。 問題的關鍵是開發(fā)者一定要搞清楚 哪些組件需要 @observer。 這個問題先放一下, 我們在看一個mobx的問題

    假設L122復用了一個第三方庫提供的組件(表明我們不能修改這個組件)

    @observer class L122 extends Component {render() {console.log('invoke L122')const x = window.store // 直接從Mobx獲取return (<div><BigComponent x={x}/></div>)} } 復制代碼

    組件 BigComponent 正如其名 是一個很‘大’的組件,他接收一個props對象 x,x結構如下:

    x = {name: 'n'addr: '', } 復制代碼

    此時當我們執(zhí)行: window.store.x.name = 'fcdcd' 的時候, 我們期待的是BigComponent按照我們的意愿,根據(jù)改變后的x重新渲染, 其實不會。 因為在這里沒有任何組件 依賴name, 為了讓L122 正常工作, 我們必須:

    @observer class L122 extends Component {render() {console.log('invoke L122')const x = window.store.x const nx = {name: x.name,addr: x.addr}return (<div><BigComponent x={nx}/></div>)} } 復制代碼

    如果不明白mobx的原理, 可能會很疑惑,疑惑這里為什么要這么寫, 疑惑哪里為啥不更新, 疑惑哪里為啥莫名其妙更新了。。。

    什么組件需要@observer? 當一個render方法里,出現(xiàn)我們不能控制的組件(包括原生標簽, 第三方庫組件)依賴于狀態(tài)的時候, 我們應該使用@observer, 其他組件應該繼承PureComponent。 這樣我們的應用在狀態(tài)發(fā)送改變的時候,更新的集合最小,性能最高。

    除此之外,Mobx還有一個性能隱患,希望mobx的擁護者能夠清楚的認知到,假設現(xiàn)在 L122 不僅也依賴title, 還依賴狀態(tài)a, b, c, d, e, f, g, h:

    class L122 extends Component {render() {console.log('invoke L122')const { title, a, b, c, d, e, f, g, h } = window.storereturn (<div><span>{title}</span><span>{a}</span><span>{b}</span>...<span>{h}</span></div>)} }function changeValue() {window.store.title = 't'window.store.a = 'a1'window.store.b = 'b1'window.store.c = 'c1' } 復制代碼

    當執(zhí)行 changeValue()的時候 會發(fā)生什么呢?控制臺會打印:

    invoke MobxRoot invoke L122 invoke L122 invoke L122 invoke L122 復制代碼

    一身冷汗!!得好好想想這里的數(shù)據(jù)層設計, 是否把這幾個屬性組成一個對象,狀態(tài)越來越復雜的時候可能不是那么簡單。

    第三方庫結合

    redux與第三方庫結合沒有好說的,工作的很好。 很多庫現(xiàn)在已經(jīng)假定了 傳人的狀態(tài)是 不可變的。

    mobx正如前文所說 不管是發(fā)布為第三方庫, 還是使用第三方庫

  • mobx寫的組件,發(fā)布給其他應用使用比較困難,因為要不我們直接從全局取數(shù)據(jù)渲染(context獲取 道理相同), 要不推遲引用值的獲取, 不管是哪一種,組件都沒有任何可讀性。
  • mobx 使用第三方 例如BigComponent, 沒有那么自然。
  • 開發(fā)效率

    這里我們只說 immutable的開發(fā)效率,mutable的開發(fā)效率應該是最低的。 0. 結合對象展開浮, js裸寫。 也不難

  • immutable.js 學習成本略高, 包大小也畢竟大
  • 函數(shù)式編程,項目組自己一個人 可以考慮
  • immer 如果不考慮IE,強烈推薦, 強烈推薦 (作者是mobx的作者)。 immer和mutable的修改數(shù)據(jù)的方法是一摸一樣的, 最后會根據(jù)你的修改返回一個不可變的對象。 github地址
  • 結論

    如果你能無痛的處理immutable, 那么Redux + PureComponent 很方便寫出高性能的應用。

    如果你對Mobx掌握的足夠好, 那么Mobx絕對會迅速的提高開發(fā)效率。

    本文代碼github地址


    總結

    以上是生活随笔為你收集整理的Redux vs Mobx系列(-):immutable vs mutable的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。