Principio de implementación de MobX revelado

mobx es una biblioteca de administración de estado popular, solo superada por redux en popularidad. Es lo mismo que redux en algunos lugares, y no es lo mismo en algunos lugares:

Lo mismo es que tanto mobx como redux son un flujo de datos unidireccional, activando actualizaciones de estado global a través de acciones y luego notificando vistas.

El flujo de datos de redux:

El flujo de datos de mobx:

Pero modifican el estado de manera diferente:

Redux devuelve un nuevo estado cada vez y generalmente se usa con bibliotecas que implementan objetos inmutables.

mobx es el mismo objeto de estado que se modifica cada vez, según el proxy receptivo, es decir, el proxy Object.defineProperty obtiene, configura el procesamiento, recopila dependencias cuando se obtiene, notifica a todas las dependencias para que se actualicen cuando se modifica el conjunto. Es muy similar al proxy reactivo de vue2.

Entre ellos, el método redux es una idea funcional, por lo que la modificación del estado está en cada función reductora, y el método mobx es la idea de un proxy orientado a objetos, por lo que es fácil organizar el estado en una clase.

Esto también conduce a una diferencia en la organización del código de los dos métodos de gestión de estado:

Redux organiza el estado en funciones reductoras (características funcionales):

const reducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT': return state + 1;
    case 'DECREMENT': return state - 1;
    default: return state;
  }
};
复制代码

Mobx, por otro lado, organiza el estado en clases (características orientadas a objetos):

import {observable, action} from 'mobx';
class Store {
  @observable number = 0;
  @action add = () => {
    this.number++;
  }
}
复制代码

Además, el método redux devuelve un nuevo objeto cada vez.Aunque la biblioteca inmutable se puede usar para reducir la sobrecarga de crear nuevos objetos, la sobrecarga sigue siendo un poco mayor que mobx modificando directamente el objeto original.

Además, redux notifica todas las dependencias cuando se actualizan, y mobx puede notificar con precisión porque recopila las dependencias de cada atributo.

Entonces, el rendimiento de mobx será mayor que el de redux.

综上,mobx 和 redux 都是单向数据流,但是管理状态的思路上,一个是函数式的思想,通过 reducer 函数每次返回新的 state,一个是面向对象的思想,通过响应式对象来管理状态,这导致了状态组织方式上的不同(function/class),而且 redux 创建新的 state 的开销还有通知所有依赖的开销都比 mobx 大,性能比 mobx 差一些。

对比下来,我们会发现 mobx 似乎比 redux 优秀一些。那我们具体看下 mobx 怎么用吧:

mobx 的使用

官方提供的 demo 是这样的:

import React from "react"
import ReactDOM from 'react-dom';
import { makeAutoObservable } from "mobx"
import { observer } from "mobx-react"

class Timer {
    secondsPassed = 0

    constructor() {
        makeAutoObservable(this)
    }

    increase() {
        this.secondsPassed += 1
    }

    reset() {
        this.secondsPassed = 0
    }
}

const myTimer = new Timer()

const TimerView = observer(({ timer }) => (
    <button onClick={() => timer.reset()}>Seconds passed: {timer.secondsPassed}</button>
))

setInterval(() => {
    myTimer.increase()
}, 1000);

ReactDOM.render(<TimerView timer={myTimer} />, document.getElementById('root'));
复制代码

就像前面说的,mobx 基于响应式对象来管理状态,所以组织状态是用 class 的形式。

我们声明了 Timer 的 class,有一个属性是 secondsPassed 代表过去了几秒,有两个方法来修改它。

在构造器里调用 makeAutoObservable 来创建响应式的代理。

然后 new 一个 Timer 的对象,传到组件里,组件使用 observer 的高阶组件包裹,它负责把被包裹的组件添加到 timer 的响应式依赖中去。

然后把这个组件渲染到 dom。

这样就完成了 mobx 和 react 的结合使用,看下效果:

我们是把时间(secondsPassed)放在 mobx 的全局 state 中管理的,在组件里使用,然后定时更新它。发现每次更新组件都得到了通知并做了渲染,这就是全局状态管理的功能。

demo 里我们用的 makeAutoObservable 函数,它会自动给属性添加响应式代理,方法会添加一层触发 action 的代理。

也可以手动标识:

import { observable, action } from "mobx"

class Timer {
    @observable secondsPassed = 0

    constructor() {
    }

    @action increase() {
        this.secondsPassed += 1
    }

    @action reset() {
        this.secondsPassed = 0
    }
}
复制代码

我们大概知道了 mobx 怎么用,那它是怎么实现的呢?

接下来我们从源码来理一下它的实现原理:

mobx 的实现原理

首先,mobx 会对对象做响应式代理,那代理以后的对象是什么样的呢?

我们打印下:

原始对象的 secondsPassed 属性是 0,increase 和 reset 方法体修改 secondsPassed 的值。

而代理以后的对象属性分为了 get 和 set,并且实现变成了 this[$mobx].getObservablePropValue 和 setObservablePropValue,这明显是做了响应式的处理了。

代理以后的方法都变成了 excuteAction,执行方法会 dispatch 一个 acition。

那这个响应式的代理是怎么实现的呢?

跟踪 makeAutoObservable 的源码会发现 mobx 创建了一个 ObservableObjectAdministration 的对象放到了 $mobx 属性上。

在 timer 对象确实是有这个属性的:

用 Symbol 声明了一个私有属性 mobx administration 来放 ObservableObjectAdministration 对象。

然后还用 Symbol 声明了一个私有属性。mobx-keys 来放所有做了代理的属性和方法名。

那这个 ObservableObjectAdministration 对象是干啥的呢?

看下它的定义:

可以看到它有 values 属性记录每个 key 的依赖。

还有 getObservableValue 和 setObservableValue 来获取和设置某个 key 的值。这两个方法就是被代理后的属性的 get set 最终调用的方法:

这不就串起来了么:

创建对象的时候 mobx 会对属性和方法做代理,会添加一个 Symbol(mobx administrator) 属性到对象上来保存 ObservableObjectAdministration 对象,它是用来记录属性的所有依赖的,对属性的 get 和 set 都会被代理到这个 ObservableObjectAdministration 的 getXxx 和 setXxx 方法上。

我们打印下这个对象看看:

确实,values 里保存了唯一一个属性和它的所有依赖。

至此,对对象做响应式代理的流程我们已经理清了:

那这个依赖是什么时候收集的呢?

我们继续往下看 get 收集依赖和 set 触发依赖更新的部分:

我们用 observable 包裹了组件,它是一个高阶组件,对组件做一层代理,返回新的组件:

在这层代理里面,创建了 Reaction 对象,也就是收到更新的通知之后怎么做出反应,在回调函数里用 setState([]) 的方式实现了强制更新。

并且,这层高阶组件的代理里会把当前组件设置到全局,这样后面做 get 的依赖收集的时候就能拿到对应的组件了。

所以在组件里用到 state 的 get,做依赖收集时,就知道当前是哪个组件了:

当然,这里收集的不是具体哪个组件,而是 onInvalidate 的回调函数,也就是收到更新的通知之后如何做出反应。

这样就完成了依赖的收集,在后面修改响应式对象的状态属性的时候,就会触发依赖,然后实现组件的更新:

这样,我们就串联起了 mobx 的响应式原理:

总结

mobx 是热度仅次于 redux 的状态管理库,它和 redux 有相同的地方也有不同的地方:

相同的地方是都是单向数据流。

不同的地方是 redux 是函数式思想的实现,通过 reducer 函数管理状态,一般会用 immutable 的库来提高创建新对象的性能。而 mobx 是面向对象的思想,通过响应式代理来管理状态,可以通过 class 组织 state。

性能方面 mobx 的响应式能精准的通知依赖做更新,而 redux 只能全局通知,而且 mobx 只是修改同一个对象,不是每次创建新对象,性能会比 redux 更高。

然后我们又通过一个 demo 来入门了下 react 中使用 mobx:通过 class 组织状态,然后创建响应式代理,组件用 observer 高阶组件做一层包装,传入 mobx 的对象,这样 mobx 和组件就结合到了一起,状态更新就能通知到组件。

之后我们从源码层面理清了 mobx 的响应式机制的实现原理:mobx 会在对象上添加一个 Symbol($mobx) 的隐藏属性,用来放 ObservableObjectAdministration 对象,它是用于管理属性和它的依赖的,在 get 的 时候收集依赖,然后 set 的时候就可以通知所有收集到的依赖(Reaction)做更新。

看到这里,你是否对 mobx 的特点和原理有更深的理解了呢?

Supongo que te gusta

Origin juejin.im/post/7083975275645501448
Recomendado
Clasificación