# 解析Observable

解析Observable

深度解析与理解Fast-Element中的Observable 的设计原则与思想.Fast 中的Observable是为了实现通用的观察者模型,解耦SubjectObserver.

Notifier,这个是通知模型的抽象,它提供了如下的接口约束. 它的职责是解耦订阅者与主题.同时提供了API的方式来通知被订阅者.我们可以认为它最重要的功能就通知订阅者.也就是notifiy()

内部对象抽象

  • Subject,本质是它就是被关心或者被订阅的对象
  • Subscriber, 这个就是订阅者.它需要实现handleChange(subject: any, args: any): void; 来响应主题的变化
  • subscriber|unsubscribe,这个是订阅与取消的方法.本质上是订阅者将自己交付给Notifier
  • nofity()API 发布subject变化的消息,通知订阅者
/**
 * Provides change notifications for an observed subject.
 * @public
 */
export interface Notifier {
    /**
     * The object that subscribers will receive notifications for.
     */
    readonly subject: any;

    /**
     * Notifies all subscribers, based on the args.
     * @param args - Data passed along to subscribers during notification.
     * @remarks
     * In some implementations, the args may be used to target specific subscribers.
     * This is usually in the case where a propertyName was passed during subscription.
     */
    notify(args: any): void;

    /**
     * Subscribes to notification of changes in an object's state.
     * @param subscriber - The object that is subscribing for change notification.
     * @param propertyToWatch - The name of the property that the subscriber is interested in watching for changes.
     * @remarks
     * Some implementation may or may not require the propertyToWatch.
     */
    subscribe(subscriber: Subscriber, propertyToWatch?: any): void;

    /**
     * Unsubscribes from notification of changes in an object's state.
     * @param subscriber - The object that is unsubscribing from change notification.
     * @param propertyToUnwatch - The name of the property that the subscriber is no longer interested in watching.
     * @remarks
     * Some implementation may or may not require the propertyToUnwatch.
     */
    unsubscribe(subscriber: Subscriber, propertyToUnwatch?: any): void;
}

对于该接口有如下的三种实现

Notifier
    --- SubscriberSet
        --- DefaultArrayObserver
    --- PropertyChangeNotifier
        --- ElementController
  • SubscribeSet这个是针对订阅者大概率只有1-2个场景准备的,关注性能,主题是一个整体
  • PropertyChangeNotifier这个针对主题是一个对象,订阅者可以针对对象的某个属性进行订阅的场景
  • DefaultArrayObserver这个是针对主题是一个数组的特殊场景.当数组内部发生了inline的改动也能及时通知的场景
  • ElementControllerFast 的组件本身就实现了这个接口.这个是为每个WebComponent的一个实现

Observable.getNotifer()

这个方法是针对Subject来创建或者获取对应的Notifier对象.这个对象可能缓存在内部的一个weakMap里,或者就是$fastController对象.它有可能是三种类型

  • DefaultArrayObserver
  • PropertyChangeNotifier
  • ElementController

Observable.track(source,propertyName)

这个方法其实是告诉上下文,source对象的propertyName属性被使用到了,请感兴趣的及时订阅. 这个对方有个问题,source对象不知道谁对我感兴趣,所以它只能假设上下文中有个watcher存在,并通过调用watcher.watch(source,propertyName)把自己交付给watcher.这个watcher 是通过一个模块级别的闭包来定义的.

watcher

这个watcher 是为了Fast的而准备的,在全篇它是为Expression而准备的,它观察的对象其实是表达式.表达式也是Fast 内部对于动态binding 的一种抽象.watcher的类型是ExpressionNotifierImplementation,所以它具备了如下的一些约束

  • Notifier, 这个对象本身就是一个notifer.跟确切的来说它是一个SubscribeSet,一个高效定制版的notifer
  • ExpressionObserver<TSource,TReturn,TParent>, 它本身也是一个针对表达式的观察者.这个接口只定义了一个方法bind(controller):TReturn,接管表达式的执行过程.
    • 它持有一个表达式,(source,context)=>TReturn,构造它的时候需要执行表达式
    • observe,不要被名字给诱惑,它其实是观察并且执行表达式.它会负责上下文的切换管理.执行前把当前的watcher 设置为自己,执行完后,将watcher 再还原.在这个时候实现了对于表达式执行的observe.
    const pre = watcher;
    watcher=this;
    try{
        return this.express(controller,context);
    }finally{
        watcher=pre;
    }
    
    • bind其实就是调用observe.
  • watch, 这个方法其实就是去订阅,它是由Observable.track(source,propertyName)发起的.它的职责就是对source的propertyName进行订阅.它本身就是订阅者.所以它需要实现handleChange这个方法.它订阅的目的就是执行表达式.总结一下就是,当source的propertyName 发生变化就执行表达式.为什么呢?因为这个表达式内部使用了这个source.perpertyName
  • 串起来说就是,它在执行表达式之前,先把自己设置成了上下文中的观察者,再执行表达式的时候,主题的属性通过Observable.track()通知了它自己watcher.watch(),当下次这个属性再次发生变化的时候Observable.notify()就再次执行表达式call().会把消息发送给订阅它的订阅者.
  • handleChange,它本身也是观察者.
  • 所以这个类为了实现多个功能,违反了单一职责的原则.这个其实也是为了兼顾效率.也是最晦涩难懂的地方.

总结一下这个类

  • 它代理表达式的执行过程,观察并且订阅执行过程中表达式所用到的主题属性.
  • 当这些属性下次变化时,该表达式会把这个消息转发给订阅这个表达式的订阅者.一般而言这个其实就是HTMLBindingDirective 或者说是我们在html中写的那一个箭头函数.所以订阅者大部分情况下就是一个.
  • 它既是表达式执行的观察者,也是一个主题,也是一个notifier.

Observable.notify(source,args)

这个方法会通知所有对source感兴趣的订阅者.它会用source为key 获取到notifier对象,然后调用它的notifier.notify()

DefaultObservableAccessor 跟上面的ExpressionNotifierImplementation类似,它其实是通过对属性的get/set进行拦截,来触发watcher.watch().它会以对象为key,属于的属性放到一个数组里面.这个也是@observable, defineProperty底层的真正逻辑.

binding这个就是创建ExpressionNotifierImplementation的API,

Observable.trackVolatile()这个是通知当前的watcher, 需要刷新.

@observable 这个本质上就是简单的调用Observable.defineProperty来构建DefaultObservableAccessor

@volatile 这个标记下次再次执行的时候需要重新订阅,一般情况下只需要订阅一次就行了.这部分需要集合DataBinding来看

posted @ 2025-04-19 20:34  kongshu  阅读(16)  评论(0)    收藏  举报