一抒线、認識MobX
打印
mobx
讨越,看看mobx
中有什么
MobX
的整個流程
MobX 和 Redux 的比較
-
Redux
是單一數(shù)據(jù)源,而MobX
往往是多個store
。MobX
可以根據(jù)應(yīng)用的UI
、數(shù)據(jù)或業(yè)務(wù)邏輯來組織store
,具體如何進行需要你自己進行權(quán)衡 -
Redux store
使用普通的JavaScript
對象結(jié)構(gòu)剩燥,MobX
將常規(guī)JavaScript
對象包裹,賦予observable
的能力立倍,通過隱式訂閱灭红,自動跟蹤observable
的變化。MobX
是觀察引用的口注,在跟蹤函數(shù)中(例如:computed value
变擒、reactions
等等),任何被引用的observable
的屬性都會被記錄疆导,一旦引用改變赁项,MobX
將作出反應(yīng)葛躏。注意,不在跟蹤函數(shù)中的屬性將不會被跟蹤悠菜,在異步中訪問的屬性也不會被跟蹤 -
Redux
的state
是只讀的舰攒,只能通過將之前的state
與觸發(fā)的action
結(jié)合,產(chǎn)生新的state
悔醋,因此是純凈的(pure
)摩窃。而MobX
的state
即可讀又可寫,action
是非必須的芬骄,可以直接賦值改變猾愿,因此是不純凈的(Impure) -
Redux
需要你去規(guī)范化你的state
,Immutable
數(shù)據(jù)使Reducer
在更新時需要將狀態(tài)樹的祖先數(shù)據(jù)進行復(fù)制和更新账阻,新的對象會導致與之connect
的所有UI
組件都重復(fù)渲染蒂秘。因此Redux state
不建議進行深層嵌套,或者需要我們在組件中用shouldComponentUpdate
優(yōu)化淘太。而MobX
只自動更新你所關(guān)心的姻僧,不必擔心嵌套帶來的重渲染問題
redux
管理的是 (STORE
->VIEW
->ACTION
) 的整個閉環(huán),而mobx
只關(guān)心STORE
->VIEW
的部分
優(yōu)點
-
基于運行時的數(shù)據(jù)訂閱
mobx
的數(shù)據(jù)依賴始終保持了最小蒲牧,而且還是基于運行時撇贺。而如果用redux
,可能一不小心就多訂閱或者少訂閱了數(shù)據(jù)冰抢。所以為了達到高性能松嘶,我們需要借助PureRenderMixin
以及reselect
對selector
做緩存 -
通過 OOP 的方式組織領(lǐng)域模型 (domain model)
OOP
的方式在某些場景下會比較方便,尤其是容易抽取domain model
的時候挎扰。進而由于mobx
支持引用的方式引用數(shù)據(jù)翠订,所以可以非常容易得形成模型圖 (model graph ),這樣可以更好地理解我們的應(yīng)用遵倦。 -
修改數(shù)據(jù)方便自然
mobx
是基于原生的JavaScript
對象蕴轨、數(shù)組和Class
實現(xiàn)的。所以修改數(shù)據(jù)不需要額外語法成本骇吭,也不需要始終返回一個新的數(shù)據(jù),而是直接操作數(shù)據(jù)
缺點
- 缺最佳實踐和社區(qū) mobx 比較新歧寺,遇到的問題可能社區(qū)都沒有遇到過燥狰。并且,mobx 并沒有很好的擴展/插件機制
-
隨意修改 store 我們都知道 redux 里唯一可以改數(shù)據(jù)的地方是 reducer斜筐,這樣可以保證應(yīng)用的安全穩(wěn)定龙致;而 mobx 可以隨意修改數(shù)據(jù),觸發(fā)更新顷链,給人一種不安全的感覺
- 最新的
mobx 2.2
加入了action
的支持目代。并且在開啟strict mode
之后,就只有action
可以對數(shù)據(jù)進行修改,限制數(shù)據(jù)的修改入口榛了≡谘龋可以解決這個問題
- 最新的
-
邏輯層的限制
- 如果更新邏輯不能很好地封裝在
domain class
里,用redux
會更合適霜大。另外构哺,mobx
缺類redux-saga
的庫,業(yè)務(wù)邏輯的整合不知道放哪合適
- 如果更新邏輯不能很好地封裝在
二战坤、核心API
2.1 @observable
Observable
值可以是JS基本數(shù)據(jù)類型曙强、引用類型、普通對象途茫、類實例碟嘴、數(shù)組和映射。其修飾的state會暴露出來供觀察者使用
// Observable 值可以是JS基本數(shù)據(jù)類型囊卜、引用類型娜扇、普通對象、類實例边败、數(shù)組和映射
@observable title = 'this is about page'
@observable num = 0
// 計算值(computed values)是可以根據(jù)現(xiàn)有的狀態(tài)或其它計算值衍生出的值
@computed get getUserInfo(){
return `我是computed經(jīng)過計算的getter,currenct num:${this.num}`
}
// 注意:當你使用裝飾器模式時袱衷,@action 中的 this 沒有綁定在當前這個實例上,要用過 @action.bound 來綁定 使得 this 綁定在實例對象上
@action.bound add(){
this.num ++
}
@action.bound reduce(){
this.num --
}
2.2 observer
可以用作包裹
React
組件的高階組件笑窜。 在組件的render
函數(shù)中的任何已使用的observable
發(fā)生變化時致燥,組件都會自動重新渲染。 注意observer
是由"mobx-react"
包提供的排截,而不是mobx
本身
-
@Observer
是一個注解嫌蚤,本質(zhì)上是用mobx.autorun
包裝了組件的render
函數(shù)以確保任何組件渲染中使用的數(shù)據(jù)變化時都可以強制刷新組件
2.3 @computed
- 計算值(
computed values
)是可以根據(jù)現(xiàn)有的狀態(tài)或其它計算值衍生出的值 - 用于獲取由基礎(chǔ)
state
衍生出來的值。如果基礎(chǔ)值沒有變断傲,獲取衍生值時就會走緩存脱吱,這樣就不會引起虛擬 DOM 的重新渲染
getter
:獲得計算得到的新state
并返回。setter
: 不能用來直接改變計算屬性的值认罩,但是它們可以用來作“逆向”衍生箱蝠。
通過
@computed + getter
函數(shù)來定義衍生值
class Foo {
@observable length = 2;
@computed get squared() {
return this.length * this.length;
}
set squared(value) { // 這是一個自動的動作,不需要注解
this.length = Math.sqrt(value);
}
}
2.4 @actions
- 只有在
actions
中垦垂,才可以修改Mobx
中state
的值- 注意:當你使用裝飾器模式時宦搬,
@action
中的this
沒有綁定在當前這個實例上,要用過@action.bound
來綁定 使得this
綁定在實例對象上
- 通過引入
mobx
定義的嚴格模式劫拗,強制使用action
來修改狀態(tài)
import {configure} from 'mobx';
configure({ enforceActions: 'always' }) // 開啟嚴格模式
@action.bound add(){
this.num ++
}
@action.bound reduce(){
this.num --
}
2.5 autorun
- 當可觀察對象中保存的值發(fā)生變化時间校,可以在
mobx.autorun
中被觀察到。observable
的值初始化或改變時页慷,自動運行 - 如果你想響應(yīng)式的產(chǎn)生一個可以被其它
observer
使用的值憔足,請使用@computed
胁附,如果你不想產(chǎn)生一個新值,而想要達到一個效果滓彰,請使用autorun
捉蚤。 舉例來說猴贰,效果是像打印日志鸠项、發(fā)起網(wǎng)絡(luò)請求等這樣命令式的副作用
import {
observable,
computed,
action,
autorun
} from 'mobx'
class AppState {
constructor({ count, name } = { count: 0, name: 'Jokcy' }) {
this.count = count
this.name = name
}
@observable count
@observable name
@computed get msg() {
return `${this.name} say count is ${this.count}`
}
@action add() {
this.count += 1
}
@action changeName(name) {
this.name = name
}
toJson() {
return {
count: this.count,
name: this.name,
}
}
}
const appState = new AppState()
// 一旦appState有更新執(zhí)行方法
autorun(()=>{
console.log(appState.msg)
})
export default AppState
2.6 reactions
Reactions
和計算值很像婚被,但它不是產(chǎn)生一個新的值,而是會產(chǎn)生一些副作用洗做,比如打印到控制臺弓叛、網(wǎng)絡(luò)請求、遞增地更新React
組件樹以修補DOM
诚纸、等等撰筷。 簡而言之,reactions
在 響應(yīng)式編程和命令式編程之間建立溝通的橋梁
2.7 Flow
用法:
flow(function* (args) { })
-
flow()
接收generator
函數(shù)作為它唯一的輸入
import { configure } from 'mobx';
// 不允許在動作外部修改狀態(tài)
configure({ enforceActions: true });
class Store {
@observable githubProjects = [];
@observable state = "pending"; // "pending" / "done" / "error"
fetchProjects = flow(function* fetchProjects() { // <- 注意*號畦徘,這是生成器函數(shù)毕籽!
this.githubProjects = [];
this.state = "pending";
try {
const projects = yield fetchGithubProjectsSomehow(); // 用 yield 代替 await
const filteredProjects = somePreprocessing(projects);
// 異步代碼自動會被 `action` 包裝
this.state = "done";
this.githubProjects = filteredProjects;
} catch (error) {
this.state = "error";
}
})
}
三、計數(shù)器例子
import React, { Component } from 'react';
import { observer } from 'mobx-react';//結(jié)合react
import { observable, autorun,computed } from 'mobx';
// 定義數(shù)據(jù)store
class Counter {
@observable number = 0;
@computed get msg() {
return 'number:' + this.number
}
// 用action改變數(shù)據(jù)井辆,避免混亂
@action increment(){
this.number ++
}
@action decrement: () => {
this.number --
}
}
var store = new Counter()
// 把屬性注入react組件
@observer
class App extends Component {
render() {
return (<div>
{ store.msg } <br />
<button onClick={this.handleInc}> + </button>
<button onClick={this.handleDec}> - </button>
</div>);
}
handleInc() {
store.increment();
}
handleDec() {
store.decrement();
}
}
ReactDOM.render(<App />, document.getElementById('root'));