react 精华之mobx 意味着只要我们state的状态在store中被修改了 this.count 这个 参数就会被监听到 就可以直接渲染页面了

理解 Mobx

虽然 Mobx 和 Redux 有很大不同,但是至少还有一个共同点——这两个工具都和 React 没有任何直接关系,只不过凑巧 React 社区大量使用它们罢了。从技术上说,Mobx 和 Redux 都是中立的状态管理工具,他们能够应用于 React,也可以用于其他需要状态管理的场景。

还是用 create-react-app 产生的应用来演示 Mobx 吧,要使用 Mobx,首先我们要在项目中安装对应的 npm 包。

npm install mobx

我们用 Mobx 来实现一个很简单的计数工具,首先,需要有一个对象来记录计数值,代码如下:

import {observable} from 'mobx';

const counter = observable({
  count: 0
});

在上面的代码中,counter 是一个对象,其实就是用 observable 函数包住一个普通 JavaScript 对象,但是 observable 的介入,让 counter 对象拥有了神力。

我们用最简单的代码来展示这种“神力”,代码如下:

import {autorun} from 'mobx';

window.counter = counter;

autorun(() => {
  console.log('#count', counter.count);
});

把 counter 赋值给 window.counter,是为了让我们在 Chrome 的开发者界面可以访问。用 autorun 包住了一个函数,这个函数输出 counter.count 的值,这段代码的作用,我们很快就能看到。

在 Chrome 的开发者界面,我们可以直接访问 window.counter.count,神奇之处是,如果我们直接修改 window.counter.count 的值,可以直接触发 autorun 的函数参数!

 

这个现象说明,mobx 的 observable 拥有某种“神力”,任何对这个对象的修改,都会立刻引发某些函数被调用。和 observable 这个名字一样,被包装的对象变成了“被观察者”,而被调用的函数就是“观察者”,在上面的例子中,autorun 的函数参数就是“观察者”。

Mobx 这样的功能,等于实现了设计模式中的“观察者模式”(Observer Pattern),通过建立 observer 和 observable 之间的关联,达到数据联动。不过,传统的“观察者模式”要求我们写代码建立两者的关联,也就是写类似下面的代码:

observable.register(observer);

Mobx 最了不起之处,在于不需要开发者写上面的关联代码,Mobx自己通过解析代码就能够自动发现 observer 和 observable 之间的关系。

我们很自然想到,如果让我们的数据拥有这样的“神力”,那我们就不用在修改完数据之后,再费心去调用某些函数使用这些数据了,数据管理会变得十分轻松。

 

decorator

因为 Mobx 的作用就是把简单的对象赋予神力,总要有一种方法能够在不改变对象代码的前提,去改变对象的行为,这就用得上“装饰者模式”(Decorator Pattern)。

单独说“装饰者模式”,这只是面向对象编程思想下的一种模式,不过对 JavaScript 语言而言,就不只是一种模式,而是一种语言特性,它在语法上对这种模式提供了强大的支持,所谓强大,就是指使用起来代码极其简洁。

根据 JavaScript 语法,我们可以这样创造一个 decorator,叫做 log

function log(target, name, descriptor) {
  console.log('#target', target);
  console.log('#name', name);
  console.log('#descriptor', descriptor);
  return descriptor;
}

当然,很明显这个 decorator 什么实质的事情都没做,只是用 console.log 输出了三个参数秀了一下存在感,最后返回的 descriptor,就是被这个『装饰者』所『装饰』的对象。

下面是使用这个 decorator 的代码示例:

@log
class Bar {
  @log
  bar() {
    console.log('bar');
  }
}

 

可以看到,@ 符号就是使用 decorator 的标志,将 @log 作用于一个类 Bar,那么最后得到的 Bar 其实是调用 log 函数返回的结果;将 @log 作用于一个类成员 @bar,最后得到的 bar 同样是调用 log 函数之后得到的结果。可见,如果我们巧妙地编写 log 函数,控制返回的结果,就可以操纵被『装饰』的类或者成员。

 

在 create-react-app 中增加 decorator 支持

很不幸,create-react-app 产生的应用并不支持 decorator,官方解释 是:decorator 并没有成为稳定的标准,为了防止今天写的代码没多久就不好使,create-react-app 不会支持这样的不稳定的功能。

不过,这并不表示完全没有办法,事情可以解决,只是有些麻烦,我们要做的只是在应用中添加支持 decorator 的 babel plugin。

首先,我们动用 create-react-app 的 eject 功能,这表示我们和 create-react-app 缺省照顾一切的 react-scripts 一刀两断,从此之后,webpack 和 babel 配置就完全由我们自己控制。要注意,eject 是不可逆的,做了就收不回来了,所以,请谨慎使用 eject,不过,为了支持 decorator,我们也别无选择。

npm run eject

 

在 eject 之后,我们手动安装支持 decorator 的 babel 插件:

npm install --save-dev babel-plugin-transform-decorators-legacy 

然后,我们找到 package.json 里面 babel 这一部分,添加 plugins 部分,让这一部分变成这个样子:


  "babel": {
    "plugins": [
      "transform-decorators-legacy"
    ],
    "presets": [
      "react-app"
    ]
  },

现在,我们离在 create-react-app 产生的应用中使用 decorator 只差一步了,记得我们说过 Mobx 和 React 并无直接关系吗?为了建立二者的关系,还需要安装 mobx-react:

npm install mobx-react

现在,我们可以使用 decorator 来操作 Mobx了。 decorator相当于是观察者模式的外壳

下面是重点部分

用 decorator 来使用 Mobx

还是以 Counter 为例,看如何用 decorator 使用 Mobx,我们先看代码:

import {observable} from 'mobx';
import {observer} from 'mobx-react';

@observer
class Counter extends React.Component {
  @observable count = 0;

  onIncrement = () => {
    this.count ++;
  }

  onDecrement = () => {
    this.count --;
  }

  componentWillUpdate() {
    console.log('#enter componentWillUpdate');
  }

  render() {
    return(
      <CounterView
        caption="With decorator"
        count={this.count}
        onIncrement={this.onIncrement}
        onDecrement={this.onDecrement}
        />
    );
  }
}

在上面的代码中,Counter 这个 React 组件自身是一个 observer,而 observable 是 Counter的一个成员变量 count

注意 observer 这 个decorator 来自于 mobx-react,它是 Mobx 世界和 React 的桥梁,被它“装饰”的组件,只要用到某个被 Mobx 的 observable “装饰”过的数据,自然会对这样的数据产生反应。所以,只要 Counter 的 count 成员变量一变化,就会引发 Counter 组件的重新渲染。

可以注意到,Counter 的代码中并没有自己的 state,其实,被 observer 修饰过之后,Counter被强行"注入”了 state,只不过我们看不见而已。

意味着只要我们state的状态在store中被修改了 this.count 这个 参数就会被监听到 就可以直接渲染页面了

store实践:

独立的 Store

虽然把 observer 和 observable 集中在一个 React 组件中可行,但是,这也让 observable 的状态被封存在了 React 组件内,那我们直接用 React 自身的 state 管理也能解决问题,所以,这样使用 Mobx 意义不大。

更多适用于 Mobx 的场合,就和适用于 Redux 的场合一样,是一个状态需要多个组件共享,所以 observable 一般是在 React 组件之外。

我们重写一遍 Counter 组件,代码如下:

const store = observable({
  count: 0
});
store.increment = function() {
  this.count ++;
};
store.decrement = function() {
  this.count --;
}

@observer // this decorator is must
class Counter extends React.Component {
  onIncrement = () => {
    store.increment();
  }

  onDecrement = () => {
    store.decrement();
  }

  render() {
    return(
      <CounterView
        caption="With external state"
        count={store.count}
        onIncrement={this.onIncrement}
        onDecrement={this.onDecrement}
        />
    );
  }
}

可以看到,我们把 count 提到组件之外,甚至就把它叫做 store,这延续的是 Redux 的命名方法。

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值