React的render是 狀態 轉化爲樹狀結構的渲染組件的方法
而MobX提供了一種存儲,更新 狀態 的方法
React 和 MobX都在優化着軟件開發中相同的問題。
React 使用的方法是讓虛擬DOM來減小繁瑣而沉重的DOM變化。
而MobX則經過一個虛擬的狀態依賴圖表來讓react組件和應用狀態同步化來減小沒必要要的狀態致使組件更新javascript
MobX:前端
npm install mobx --save
React bindings:java
npm install mobx-react --save
MobX看起來很複雜的樣子,實際上是用它只須要三步react
import {observable} from 'mobx' let appState = observable({ timer: 0 })
import {observer} from 'mobx-react'; @observer class TimerView extends React.Component { render() { return (<button onClick={this.onReset.bind(this)}> Seconds passed: {this.props.appState.timer} </button>); } onReset () { //appState.resetTimer會在下一節完成 this.props.appState.resetTimer(); } }; React.render(<TimerView appState={appState} />, document.body);
appState.resetTimer = action(function reset() { appState.timer = 0; }); setInterval(action(function tick() { appState.timer += 1; }), 1000);
其中action包裝用法只能在strict模式下使用,請記得在你的javascript文件頭寫上:'use strict'。git
從上面的例子能夠看到,MobX的API其實很少:observable, computed, reactions, actionses6
其中的value能夠是JS原定的數據結構,引用,對象,數組,ES6的mapgithub
如下是一些例子:npm
const map = observable(asMap({ key: "value"})); map.set("key", "new value"); const list = observable([1, 2, 4]); list[2] = 3; const person = observable({ firstName: "Clive Staples", lastName: "Lewis" }); person.firstName = "C.S."; const temperature = observable(20); temperature.set(25);
import {observable} from "mobx"; class OrderLine { @observable price:number = 0; @observable amount:number = 1; constructor(price) { this.price = price; } //這裏在下一節會說到 @computed get total() { return this.price * this.amount; } } const line = new OrderLine(); console.log("price" in line); // true //hasOwnProperty:判斷一個對象是否有你給出名稱的屬性或對象。須要注意,此方法沒法檢查該對象的原型鏈中是否具備該屬性 console.log(line.hasOwnProperty("price")); //false
若是你的環境不支持ES6/7的語法的話,其實@observable key = value; 只是extendObservable(this, { key: value })的語法糖。所以在ES5環境下你也能使用json
Computed values 就像一個算術公式同樣去從現有的狀態或其餘值去計算出須要的值。計算的耗費是不可低估的。Computed儘量幫你減小其中的耗費。它們是高度優化的,請把它用在可能用到的地方。redux
不要混淆下一節說到的autorun。雖然他們都是被動調用的表達式。可是……
Computed使用狀況:若是你須要產生一個有觀察者(observers)參數計算的新的值的時候
autorun使用狀況:你不想產生一個新的值就想達到一個新的效果/功能。就像是打log或者進行網絡請求
Computed values是自動幫你從你的狀態(state)值和其餘計算輔助值來計算的。MobX作了不少的優化。當參與計算的值沒有發生改變,Computed是不會從新運行。若是參與計算的值沒有被使用,Computed values是暫停的。
若是Computed values再也不是觀察者(observed),那麼在UI上也會把它除掉,MobX能自動作垃圾回收。autorun則須要你本身手動去處理。若是參與計算的值再也不被使用,是不會緩存Computed的,因此從新計算是須要的。這個是最理想的默認狀況。若是你想保留,能夠了解一下keepalive和observe。
例子1: 在2.2的例子。@computed get
例子2: @computed set
class Foo { @observable length: 2, @computed get squared() { return this.length * this.length; } set squared(value) { //this is automatically an action, no annotation necessary this.length = Math.sqrt(value); } }
須要注意的是:setter並不是用於直接改變參數計算的值,如例子中的length。而是做爲一個逆推導。
Autorun是用在一些你想要產生一個不用觀察者參與的被動調用函數裏面。當autorun被使用的時候,一旦依賴項發生變化,autorun提供的函數就會被執行。與之相反的是,computed提供的函數只會在他有本身的觀察員(observers)的時候纔會評估是否從新執行,不然它的值被認爲是無用的。
根據這些經驗:若是你須要一個自動運行但卻不會產生任何新的值的結果的函數,那麼請使用Autorun。其餘狀況請使用computed。Autorun只是做用於若是達到某個效果或者功能,而不是計算某些值。若是有一個字符串做爲第一個參數存入Autorun,那麼它將成爲一個調試名稱。
var numbers = observable([1,2,3]); var sum = computed(() => numbers.reduce((a, b) => a + b, 0)); var disposer = autorun(() => console.log(sum.get())); // prints '6' numbers.push(4); // prints '10'
import {observer} from "mobx-react"; var timerData = observable({ secondsPassed: 0 }); setInterval(() => { timerData.secondsPassed++; }, 1000); @observer class Timer extends React.Component { render() { return (<span>Seconds passed: { this.props.timerData.secondsPassed } </span> ) } }); React.render(<Timer timerData={timerData} />, document.body);
tips: 若是還有其餘的decorators一塊兒或者高階組件的存在,請確保observer爲最內層(優先應用)的修飾器。不然它可能沒法工做。若是你只在ES5的環境下工做:其實observer不過是observer(class Timer ... { }) 的語法糖。
React.render(<Timer timerData={timerData.secondsPassed} />, document.body)
這時候程序並不會工做了。傳入組件的只是timerData裏面secondsPassed的當前值。在組件裏面,它是不可變的。
import {observer} from "mobx-react" import {observable} from "mobx" @observer class Timer extends React.Component { @observable secondsPassed = 0 componentWillMount() { setInterval(() => { this.secondsPassed++ }, 1000) } render() { return (<span>Seconds passed: { this.secondsPassed } </span> ) } }) React.render(<Timer />, document.body)
const colors = observable({ foreground: '#000', background: '#fff' }); const App = () => <Provider colors={colors}> <app stuff... /> </Provider>; const Button = observer(["colors"], ({ colors, label, onClick }) => <button style={{ color: colors.foreground, backgroundColor: colors.background }} onClick={onClick} >{label}<button> ); // later.. colors.foreground = 'blue'; // all buttons updated
import {observer} from "mobx-react"; @observer class TodoView extends React.Component { componentWillReact() { console.log("I will re-render, since the todo has changed!"); } render() { return <div>this.props.todo.title</div>; } }
componentWillReact沒有任何參數,並且不會在render初始化以前執行(componentWillMount的區別)。而當接收新的屬性或者setState以後,它會被調用。
action(fn) action(name, fn) @action classMethod() {} @action(name) classMethod () {} @action boundClassMethod = (args) => { body } @action(name) boundClassMethod = (args) => { body } @action.bound classMethod() {} @action.bound(function() {}) @action createRandomContact() { this.pendingRequestCount++; superagent .get('https://randomuser.me/api/') .set('Accept', 'application/json') .end(action("createRandomContact-callback", (error, results) => { if (error) console.error(error) else { const data = JSON.parse(results.text).results[0]; const contact = new Contact(this, data.dob, data.name, data.login.username, data.picture) contact.addTag('random-user'); this.contacts.push(contact); this.pendingRequestCount--; } } ))}
@action /*optional*/ updateDocument = async () => { const data = await fetchDataFromUrl(); /* required in strict mode to be allowed to update state: */ runInAction("update state after fetching data", () => { this.data.replace(data); this.isSaving = true; }) }
class Ticker { @observable this.tick = 0 @action.bound increment() { this.tick++ // 'this' will always be correct } } const ticker = new Ticker() setInterval(ticker.increment, 1000)
啓動例子項目:進入