MobX - 初识响应式编程

也许今天学习 MobX 对我来说有点晚了。 但今天还是要看一看什么是 MobX 什么是响应式编程。

什么是MobX?

Simple, scalable state management. 简单,可扩展的状态管理

官方定义非常简洁,甚至没有任何关于响应式的描述。只是突出了一句状态管理 (state management)。这是为什么呢?

反过来,官方更强调状态管理

说状态管理之前,还是想说说响应式

因为在没有了解过MobX的时候,听到最多的一个词就是响应式

响应式编程(Reactive Programming)是一种编程范式,它关注于数据流和变化传播。在响应式编程中,应用程序可以轻松地响应和处理数据流中的变化,这使得应用程序能够更好地适应异步、事件驱动的环境,从而提高了可伸缩性和响应速度。

我对响应式的大白话理解(不保证正确)

编程其实很多时候是在对数据进行处理,有一种特别常见的场景,数据改变后,我们希望做一些对应的操作。可往往最难的,就是不知道数据什么时候改变?

啊,你想要写例子:

这些情况都是我们明确知道会发生的,但是不知道多会儿会发生,也不知道多会儿能收到响应。

响应式编程,就是在这些事情发生时,自动的去做一些对应的操作,就像是把一切都准备好,等着它发生一样。

那MobX的响应式是什么样子的呢?

要解释MobX的用法,需要先学会三个重要的概念:

observable

当需要跟踪某个值或者对象的变化时,可以用observable方法将其包裹,这样MobX就能在该值变化时自动完成更新或者一些其它相关的操作。

import { observable } from 'mobx';

const person = observable({
  firstName: 'John',
  lastName: 'Doe',
  age: 30,
});

observer

刚刚只是将一个对象包裹了observable并跟踪,那真正要更新或完成的操作该如何定义呢?

这时候就需要 observer,它通常是包裹一个React组件,当数据改变是,组件就会重新渲染

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

const PersonInfo = observer(({ person }) => (
  <div>
    <h1>{person.firstName} {person.lastName}</h1>
    <p>Age: {person.age}</p>
  </div>
));

export default PersonInfo;

action

action 则是用来完成修改我们刚刚追踪值的操作。

import { action } from 'mobx';

const updateAge = action((person, newAge) => {
  person.age = newAge;
});

const celebrateBirthday = action(person => {
  person.age += 1;
});

这时候给出一个完整的例子。

import React from 'react';
import ReactDOM from 'react-dom';
import { observable, action } from 'mobx';
import { observer } from 'mobx-react';

// Observable
const person = observable({
  firstName: 'John',
  lastName: 'Doe',
  age: 30,
});

// Observer
const PersonInfo = observer(({ person }) => (
  <div>
    <h1>{person.firstName} {person.lastName}</h1>
    <p>Age: {person.age}</p>
    <button onClick={() => celebrateBirthday(person)}>Celebrate Birthday</button>
  </div>
));

// Actions
const celebrateBirthday = action(person => {
  person.age += 1;
});

// Render the React component
ReactDOM.render(<PersonInfo person={person} />, document.getElementById('root'));

这样就做到了刚才所说的响应式,某个值变了,自动完成相应的操作

可能会有这样的疑问

组件的props改变,当然会重新渲染组件,MobX的意义是什么呢?

其实不然。

MobX 可以监控非props内的变量

这样即便这个值不是props中的一员,也可以做到值改变从而重新渲染组件。

Mobx 可以监控对象中的属性值改变

同样都是对象,MobX能监控到对象内部的属性改变。但如果抛开MobX,React是一个潜比较,即便是内部的值变了,其对象引用不变也不会导致组件重新渲染。

注意MobX的监控也是 浅 监控

当我们用observable来监控一个对象改变时,MobX只能监控到这个对象内部的每一属性所对应的值。

但是如果这个属性本身又是一个对象(比如数组、对象等)那MobX无法得知其内部的改变。

如果想递归知道每个属性的改变,需要使用observable.deep来进行包裹。

说说状态管理,MobX与Redux的区别是什么?

同为状态管理,MobX和Redux有何不同?

说说其他响应式编程例子

其实最近因为工作内容与先前不同的原因,阅读了一些Java的代码,发现Java也有响应式编程,看到了Project Reactor,也就是Reactor。

最开始完全没有理解,因为MobX中的响应式可以通过observable来包裹,完成数据依赖的跟踪,但是Java中的Reactor是如何完成数据跟踪,并做到响应式的呢?

可能我还是带着前端思维去理解一个后端的Reactive框架了

对于前端来说,响应式更多的是在UI上的交互,MobX关注的是某些值的监控然后做到一些对应的响应。

而后端则是关注数据操作,即便是响应式,也是数据的响应式,响应更多的是对数据的拼装、转换、过滤等。

import reactor.core.publisher.Mono;

public class CombineMonosExample {
    public static void main(String[] args) {
        Mono<String> firstName = Mono.just("John");
        Mono<String> lastName = Mono.just("Doe");

        Mono.zip(firstName, lastName)
            .map(tuple -> tuple.getT1() + " " + tuple.getT2())
            .subscribe(
                value -> System.out.println("Value: " + value),
                error -> System.err.println("Error: " + error),
                () -> System.out.println("Completed")
            );
    }
}

参考