Mobx 中,可以直接通过 observable
的方式来控制内部的 state,而不再使用 React 自带的 state 功能。一般的写法如下:
import React from 'react';
import { observer } from 'mobx-react';
import { observable } from 'mobx';
@observer
class Demo extends React.Component {
@observable num: number = 1;
onClick = () => {
this.num += 1;
};
render() {
return (
<button onClick={this.onClick}>Clicked: {this.num}</button>
)
}
}
这样写的优势在于,可以将何时渲染的判断交给了 Mobx 去处理,不用手动去处理。
对于需要用到 observable
组合数据的情况,可以使用 computed
来生成一个新的 observable
值,也可以直接使用 getter 函数。以下的两个方案在效果上是等价的:
@observer
class Demo extends React.Component {
@observable num: number = 1;
onClick = () => {
this.num += 1;
};
get isMany() {
return this.num > 5;
}
render() {
return (
<button onClick={this.onClick}>
Clicked {this.isMany ? 'many' : 'few'} times
</button>
);
}
}
import { observable, computed } from 'mobx';
@observer
class Demo extends React.Component {
@observable num: number = 1;
onClick = () => {
this.num += 1;
};
@computed
get isMany() {
return this.num > 5;
}
render() {
return (
<button onClick={this.onClick}>
Clicked {this.isMany ? 'many' : 'few'} times
</button>
);
}
}
之所以两者是等价的,理由很简单。在执行 render 函数的时候,Mobx 注意到 this.isMany
被使用了,而在调用这个 getter 函数的时候,实际使用到了 this.num
这个 observable。因此,当 this.num
发生了变化之后,Mobx 知道需要重新调用 render 函数进行绘制。而对于使用了 computed 的情况来说,情况会更简单一些,this.num
这个 observable 的变化触发了 this.isMany
的重新计算,最终在 this.isMany
值变化之后触发了 render 函数的重新计算。
然而需要注意的一点是,两者只是在效果上等价。在实际运算过程中,computed 的方案有两个优势:
- 代码看上去更清晰。render 是因为 computed 的数据触发的,这一点在代码上可以很容易的看出来;而第一种方案,是否触发 getter 函数,其实需要多思考一下才能确定;
- 实际执行效率更高。使用 getter 的方案,由于 render 函数实际上是和
this.num
这个 observable 进行关联的,因此哪怕this.isMany
这个 getter 函数没有发生值的变化,只要this.num
变了,render 函数都需要被执行;而对于使用 computed 的情况,因为 render 是和this.isMany
进行关联的,实际this.isMany
没有变化的时候,是不需要触发重绘的。换句话说,前者 getter 的方案,在this.num
从 1 涨到 6 的过程中,一共触发了五次重新渲染;而后者computed
的方案,只触发了一次重新渲染(当this.num = 6
的时候)
针对第二点,Mobx 的 GitHub issue 中作者也有相关的说明,见这里。