computed and getter in Mobx

JavaScript

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 的方案有两个优势:

  1. 代码看上去更清晰。render 是因为 computed 的数据触发的,这一点在代码上可以很容易的看出来;而第一种方案,是否触发 getter 函数,其实需要多思考一下才能确定;
  2. 实际执行效率更高。使用 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 中作者也有相关的说明,见这里