proxy包装监听数据变化

433 阅读1分钟
import { BehaviorSubject, Subscription } from 'ims-rxjs';
import { filter } from 'ims-rxjs/operators';

export interface EventData<T> {
  evetntName: string;
  object: T;
}

export interface PropertyChangeData<T> extends EventData<T> {
  propertyName?: string;
  value?: any;
  oldValue?: any;
}

export function createObservableObject<T extends object>(
  source: T,
): T & {
  subscribe: (
    next?: (value: T) => void,
    error?: (error: any) => void,
    complete?: () => void,
  ) => Subscription;
  unsubscribe(): void;
} {
  let event: BehaviorSubject<PropertyChangeData<T>> = new BehaviorSubject({
    evetntName: 'init',
    object: undefined,
  });
  let subscription = new Subscription();
  return new Proxy(source, {
    get(target: T, p: PropertyKey, receiver: any): any {
      if (p === 'subscribe') {
        return (
          next?: (value: any) => void,
          error?: (error: any) => void,
          complete?: () => void,
        ) => {
          let subs = event
            .pipe(
              filter(
                (res: PropertyChangeData<any>) => res.evetntName !== 'init',
              ),
            )
            .subscribe(
              next,
              e => {
                subscription.remove(subs);
                error(e);
              },
              () => {
                subscription.remove(subs);
                complete();
              },
            );
          subscription.add(subs);
        };
      } else if (p === 'complete') {
        return () => subscription.unsubscribe();
      }
      let _v = Reflect.get(target, p, receiver);
      if (_v && typeof _v === 'object') {
        let val = createObservableObject(_v);
        subscription.add(
          val.subscribe(res => {
            event.next({
              ...res,
              object: source,
              propertyName: `${p as string}.${res.propertyName}`,
            });
          }),
        );
        return val;
      }
      return _v;
    },
    deleteProperty(target: T, p: PropertyKey): boolean {
      event.next({
        evetntName: 'drop',
        object: source,
        propertyName: p as string,
        value: null,
        oldValue: Reflect.get(target, p),
      });
      return Reflect.deleteProperty(target, p);
    },
    set(target: any, p: PropertyKey, value: any, receiver: any) {
      if (!Reflect.has(target, p)) {
        event.next({
          evetntName: 'add',
          object: source,
          propertyName: p as string,
          value: value,
          oldValue: null,
        });
        return Reflect.set(target, p, value, receiver);
      }
      let oldValue = Reflect.get(target, p);
      if (oldValue === value) {
        return false;
      }
      event.next({
        evetntName: 'update',
        object: this,
        propertyName: p as string,
        value: source,
        oldValue: oldValue,
      });
      return Reflect.set(target, p, value, receiver);
    },
  });
}

/**
 * 结构体
 */
let obs = createObservableObject({
  title: {
    name: 'name1',
  },
});

obs.subscribe(res => {
  console.log(res);
});
obs.title.name = 'title2';
delete obs.title.name;
obs.title.name = 'titl3';

/**
 * 数组
 */
let obs2 = [1, 2, 3, 4];
let obs3 = createObservableObject(obs2);
obs3.subscribe(res => {
  console.log(res);
  debugger;
});
obs3[0] = 2;