# 解析Observable
解析Observable
深度解析与理解Fast-Element中的Observable 的设计原则与思想.Fast 中的Observable是为了实现通用的观察者模型,解耦Subject与Observer.
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的改动也能及时通知的场景ElementController
Fast 的组件本身就实现了这个接口.这个是为每个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()