mobx源码解读-observable

516 阅读6分钟

1.mobx的基本概念

Observable 被观察者

Observer 观察

Reaction 响应

var student = mobx.observable({ name: '张三', }); mobx.autorun(() => { console.log('张三的名字:', name); });

2.mobx的原理

1.在响应式函数中(如以上autorun中通常会访问一个或多个observable对象),

  • 1)autorun首先创建一个Reaction类型的实例对象reaction,通过参数track一个响应式函数的回调函数。

  • 2)然后执行reaction.schedule_方法,执行回调函数,回调函数中调用被观察者observable.get方法,触发reportObserved方法。

  • 3)reportObserved方法中会将observavle对象收集到globalState.trackingDerivation.newObserving_队列中(globalState.trackingDerivation此时等同于reaction对象)

  • 4)处理reaction和observable的依赖关系,遍历reaction.newObserving_属性,在newObserving_队列中的每一个observable.observers_属性中添加当前reaction对象。

2.被观察者observable的value发生变化,调用observable对象set方法,触发propagateChange方法。propagateChange方法中,遍历observable.observers_属性依次执行reaction.onBecomeStale方法,再次将以上的2)3)4)执行一遍。

------------------------------------------------------------------

开始看源码

3.observable的别名createObservable

export const observable: IObservableFactory &
    IObservableFactories & {
        enhancer: IEnhancer<any>
    } = createObservable as any // observable的别名createObservable

// 将observableFactories的属性复制一份给observable
Object.keys(observableFactories).forEach(name => (observable[name] = observableFactories[name]))

1)首先 observable 是函数函数同 createObservable。

2)observable复制了observableFactories的属性。

function createObservable(v: any, arg2?: any, arg3?: any) {
    // @observable someProp;
    if (typeof arguments[1] === "string" || typeof arguments[1] === "symbol") {
        return deepDecorator.apply(null, arguments as any)
    }

    // it is an observable already, done
    if (isObservable(v)) return v

    // something that can be converted and mutated?
    const res = isPlainObject(v)
        ? observable.object(v, arg2, arg3)
        : Array.isArray(v)
        ? observable.array(v, arg2)
        : isES6Map(v)
        ? observable.map(v, arg2)
        : isES6Set(v)
        ? observable.set(v, arg2)
        : v

    // this value could be converted to a new observable data structure, return it
    if (res !== v) return res
}

createObservable方法起到了转发的作用,将传入的对象转发给具体的转换函数。

简单分析一下具体的转化模式

1)arguments[1] === "string" || typeof arguments[1] === "symbol" 采用的是装饰器@observable,装饰器的参数(target,prop,descriptor)其中arguments[1] 也就是prop为属性名称为字符串类型

2)isObservable(v) 已经转换为观察值了不需要再转换

3)observable.object、observable.array、observable.map、observable.set根据传入参数的类型分别调用具体的转换方法

4)针对原始类型提示用户建议使用observable.box方法

4.observable.box

observable.box在文档中是这样介绍的。

observable.box把普通的值转换成可观察的值,如下例。

const name = observable.box("张三");

console.log(name.get());
// 输出 '张三'

name.observe(function(change) {
    console.log(change.oldValue, "->", change.newValue);
});

name.set("李四");
// 输出 '张三 -> 李四'

observable.box retrun 一个ObservableValue类型的对像。

box<T = any>(value?: T, options?: CreateObservableOptions): IObservableValue<T> {
    const o = asCreateObservableOptions(options) // 格式化入参
    // ObservableValue的拥有方法get set observe intercept...
    return new ObservableValue(value, getEnhancerFromOptions(o), o.name, true, o.equals)
},

案例中的“name.set("李四")”,就是调用了ObservableValue的set方法。一会再介绍ObservableValue的时候会重点说下。

5.核心类 ObservableValue

ObservableValue 继承了 Atom原子类,先梳理一下Atom和ObservableValue和有什么主要能力。

Autom
 
public reportObserved(): boolean {
    return reportObserved(this)
}

public reportChanged() {
    startBatch()
    propagateChanged(this)
   endBatch()
}

ObservableValue

public set(newValue: T) {
    const oldValue = this.value
    newValue = this.prepareNewValue(newValue) as any
    if (newValue !== globalState.UNCHANGED) {
        const oldValue = this.value
        this.value = newValue
        this.reportChanged()
        ...
    }
}
public get(): T {
    this.reportObserved()
    return this.dehanceValue(this.value)
}

intercept
observe

其中reportObserved、propagateChanged在梳理autorun的时候介绍过。

1)reportObserved:调用观察值是用于更新derivation和observable的依赖关系。

2)propagateChanged:观察值改变时,observable对象的observers中存储的derivation,执行onBecomeStale方法,重新执行部署操作。

  1. Observablevalue的set 修改value同时调用Atom的reportChanged方法触发propagateChanged。

  2. Observablevalue的get 获取value值的同时调用Atom的reportObserved方法触发reportObserved。

所以上边案例中“name.set("李四");”会触发propagateChanged方法,会执行有依赖关系的 derivation 重新执行部署操作

接下来看一下new ObservableValue的时候干了什么?

constructor(
    value: T,
    public enhancer: IEnhancer<T>,
    public name = "ObservableValue@" + getNextId(),
    notifySpy = true,
    private equals: IEqualsComparer<any> = comparer.default
) {
    ...
    this.value = enhancer(value, undefined, name)
}
ObservableValue的构造函数中调用enhancer对value进行了处理,enhancer是通过参数是创建ObservableValue类型对象是传递的参数getEnhancerFromOptions(o)。getEnhancerFromOptions默认返回的是deepEnhancer。function getEnhancerFromOptions(options: CreateObservableOptions): IEnhancer<any> {
    return options.defaultDecorator
        ? options.defaultDecorator.enhancer
        : options.deep === false
        ? referenceEnhancer
        : deepEnhancer
}

gdeepEnhancer主要内容如下。

export function deepEnhancer(v, _, name) {
    if (isObservable(v)) return v
    if (Array.isArray(v)) return observable.array(v, { name })
    if (isPlainObject(v)) return observable.object(v, undefined, { name })
    if (isES6Map(v)) return observable.map(v, { name })
    if (isES6Set(v)) return observable.set(v, { name })
    return v
}

这个deepEnhancer是不是看上去有点眼熟,往上翻一下可以看出他和createObservable 函数十分相似,起到了转发的作用,将传入的对象转发给具体的转换函数。所以要理解observable我门主要就是要了解这些转换函数。接下来我们主要分析observable.object。

observable.object

object<T = any>(
        props: T,
        decorators?: { [K in keyof T]: Function },
        options?: CreateObservableOptions
    ): T & IObservableObject {
  
    const o = asCreateObservableOptions(options)
    if (o.proxy === false) {
        return extendObservable({}, props, decorators, o) as any
    } else {
        const defaultDecorator = getDefaultDecoratorFromObjectOptions(o)
        const base = extendObservable({}, undefined, undefined, o) as any
        const proxy = createDynamicObservableObject(base)
        extendObservableObjectWithProperties(proxy, props, decorators, defaultDecorator)
        return proxy
    }
}

o.proxy为true的时候只是多了一步Proxy,其余的工作基本相似,所以主要关注extendObservable方法就可以了。

extendObservable中调主要用了getDefaultDecoratorFromObjectOptions、asObservableObject、extendObservableObjectWithProperties方法。因为getDefaultDecoratorFromObjectOptions与extendObservableObjectWithProperties有关联,所以先来看asObservableObject,再看两外两个方法。

export function extendObservable<A extends Object, B extends Object>(
    target: A,
    properties?: B,
    decorators?: { [K in keyof B]?: Function },
    options?: CreateObservableOptions
): A & B {
    options = asCreateObservableOptions(options)
    const defaultDecorator = getDefaultDecoratorFromObjectOptions(options) // 默认返回deepDecorator装饰器
    asObservableObject(target, options.name, defaultDecorator.enhancer) // make sure object is observable, even without initial props
    if (properties)
        extendObservableObjectWithProperties(target, properties, decorators, defaultDecorator)
    return target as any
}

asObservableObject方法:

1)创建一个对象amd为ObservableObjectAdministration类的实例。

1)amd赋值给target[$mobx]

2)返回amd;

export function asObservableObject(
    target: any,
    name: PropertyKey = "",
    defaultEnhancer: IEnhancer<any> = deepEnhancer
): ObservableObjectAdministration {
    const adm = new ObservableObjectAdministration(
        target,
        new Map(),
        stringifyKey(name),
        defaultEnhancer
    )
    addHiddenProp(target, $mobx, adm)
    return adm
}

extendObservableObjectWithProperties:循环原始对象,对每一个属性值经过都decorator函数处理(decorators方法即通过getDefaultDecoratorFromObjectOptions方法获取的默认为deepDecorator,所以一回直接看deepDecorator)

export function extendObservableObjectWithProperties(
    target,
    properties, // 原对象
    decorators,
    defaultDecorator
) {
    startBatch()
    const keys = ownKeys(properties)
    
    // 循环原对象
    for (const key of keys) {
        const descriptor = Object.getOwnPropertyDescriptor(properties, key)!
        const decorator =
            decorators && key in decorators
                ? decorators[key]
                : descriptor.get
                ? computedDecorator
                : defaultDecorator
        const resultDescriptor = decorator!(target, key, descriptor, true) // 经过装饰器处理
        if (
            resultDescriptor // otherwise, assume already applied, due to `applyToInstance`
        )
            Object.defineProperty(target, key, resultDescriptor)
    }

    endBatch()
}

decorator默认为deepDecorator,我们来看一下它都干了什么。

export function createDecoratorForEnhancer(enhancer: IEnhancer<any>): IObservableDecorator {
    const decorator = createPropDecorator(
        true,
        (
            target: any,
            propertyName: PropertyKey,
            descriptor: BabelDescriptor | undefined,
            _decoratorTarget,
            decoratorArgs: any[]
        ) => {

            const initialValue = descriptor
                ? descriptor.initializer
                    ? descriptor.initializer.call(target)
                    : descriptor.value
                : undefined
                // 调用target[$mobx].addObservableProp方法
            asObservableObject(target).addObservableProp(propertyName, initialValue, enhancer)
        }
    )
    const res: any = decorator
    res.enhancer = enhancer
    return res
}

decorator中调用了target[$mobx].addObservableProp方法

addObservableProp(
    propName: PropertyKey,
    newValue,
    enhancer: IEnhancer<any> = this.defaultEnhancer
) {
    const { target } = this
    if (hasInterceptors(this)) {
        // 拦截处理
        const change = interceptChange<IObjectWillChange>(this, {
            object: this.proxy || target,
            name: propName,
            type: "add",
            newValue
        })
        if (!change) return // 拦截器返回空的时候不需要重新忽略此次修改。
        newValue = (change as any).newValue
    }
    // newValue转换成ObservableValue类型
    const observable = new ObservableValue(
        newValue,
        enhancer,
        `${this.name}.${stringifyKey(propName)}`,
        false
    )
    this.values.set(propName, observable) // 存储
    newValue = (observable as any).value 
    
    // generateObservablePropConfig方法返回以下描述符
    // { ..., get() { return this[$mobx].read(propName)  }, set(v) { this[$mobx].write(propName, v) } }
    Object.defineProperty(target, propName, generateObservablePropConfig(propName)) // target生成propName属性
    const notify = hasListeners(this)
    const change = {
          type: "add",
          object: this.proxy || this.target,
          name: propName,
          newValue
      }
    this.keysAtom.reportChanged() // this.keysAtom即Atom的实例
}

addObservableProp方法

1)调用ObservableValue类将newValue转换为可观察值(还记不记得上边ObservableValue调用通过enhancer调用了observable.object方法吗。现在可以看出observable.object方法中在循环对象的属性时又调用了ObservableValue。通过这种递归的方式将对象的属性转换为可观察值)

2)将属性key和observable存入target[$mobx].values中

3)将原对象属性值添加到target,并通过描述符中get和set都是直接调用this[mobx\].read和this\[mobx].write方法。

4)调用原子类Atom的reportChanged,让依赖此observable对象的derivation重新执行部署操作。

综上extendObservableObjectWithProperties作用即循环原始对象,执行以上4步,实现了将原始对象的属性代理到target上,并将值转换到可观察值,存储在target[$mobx].values中。

read和write

read(key: PropertyKey) {
    return this.values.get(key)!.get()
}
 // observable.get方法
 public get(): T {
    this.reportObserved() // Atom下的reportObserved
    return this.dehanceValue(this.value)
}

read方法会根据属性名称从this.values中查找,获取到对应的observable对象再调用observable.get方法触发reportObserved

write(key: PropertyKey, newValue) {
    const instance = this.target
    const observable = this.values.get(key)
    // intercept
    if (hasInterceptors(this)) {
        const change = interceptChange<IObjectWillChange>(this, {
            type: "update",
            object: this.proxy || instance,
            name: key,
            newValue
        })
        if (!change) return
        newValue = (change as any).newValue
    }
    newValue = (observable as any).prepareNewValue(newValue)
    if (newValue !== globalState.UNCHANGED) {
        (observable as ObservableValue<any>).setNewValue(newValue)
    }
}
// observable.prepareNewValue和observable.setNewValue方法
private prepareNewValue(newValue): T | IUNCHANGED {
    if (hasInterceptors(this)) {
        const change = interceptChange<IValueWillChange<T>>(this, {
            object: this,
            type: "update",
            newValue
        })
        if (!change) return globalState.UNCHANGED
        newValue = change.newValue
    }
    // apply modifier
    newValue = this.enhancer(newValue, this.value, this.name) // 调用enhancer转换为可观察模式
    return this.equals(this.value, newValue) ? globalState.UNCHANGED : newValue
}

setNewValue(newValue: T) {
    const oldValue = this.value
    this.value = newValue
    this.reportChanged()
    if (hasListeners(this)) {
        notifyListeners(this, {
            type: "update",
            object: this,
            newValue,
            oldValue
        })
    }
}

write方法

1)调用observable.prepareNewValue方法将新的value进行转换

2)调用observable.setNewValue重新修改值

3)触发reportChanged方法。

综上总结

var student = mobx.observable({
    name: '张三',
});

mobx通过observable方法用target代理了传入的对象,赋值给student。

因此student的结构应该如下

在调用student.name时候触发会调用get=>read=>observableValue.get=>reportObserved

修改的时候 set=>write=>observableValue.setNewValue=>reportChanged

现在基本可以理解observable是autoruan之间的关系了。