import type { IObservable, TNonFunctionProperties, TObservableKeyListener, TObservableListener, TObservableState, } from '@/core/state/IObservable.ts' /** * 创建一个可观察对象,用于管理状态和事件。 * @template T - 需要处理的状态类型 * @example * interface AppState { * count: number * user: { * name: string * age: number * } * items: number[] * } * * // 创建 ObservableImpl * const obs = new ObservableImpl({ * count: 0, * user: { name: 'Alice', age: 20 }, * items: [] * }) * * // 1️⃣ 全量订阅 * const unsubscribeAll = obs.subscribe(state => { * console.log('全量订阅', state) * }, { immediate: true }) * * // 2️⃣ 单字段订阅 * const unsubscribeCount = obs.subscribeKey('count', ({ count }) => { * console.log('count 字段变化:', count) * }) * * // 3️⃣ 多字段订阅 * const unsubscribeUser = obs.subscribeKey(['user', 'count'], ({ user, count }) => { * console.log('user 或 count 变化:', { user, count }) * }) * * // 4️⃣ 修改属性 * obs.state.count = 1 // ✅ 会触发 count 和全量订阅 * obs.state.user.age = 21 // ✅ 深层对象修改触发 user 订阅 * obs.state.user.name = 'Bob' * // 语法糖:解构赋值直接赋值触发通知 * const { count, user, items } = obs.toRefsProxy() * count = 1 // 触发 Proxy set * user.age = 18 // 深层对象 Proxy 支持 * items.push(42) // 数组方法拦截触发通知 * * // 5️⃣ 数组方法触发 * obs.state.items.push(10) // ✅ push 会触发 items 的字段订阅 * obs.state.items.splice(0, 1) * * // 6️⃣ 批量修改(同一事件循环只触发一次通知) * obs.patch({ * count: 2, * user: { name: 'Charlie', age: 30 } * }) * * // 7️⃣ 解构赋值访问对象属性仍然触发订阅 * const { state } = obs * state.user.age = 31 // ✅ 会触发 user 订阅 * * // 8️⃣ 取消订阅 * unsubscribeAll() * unsubscribeCount() * unsubscribeUser() * * // 9️⃣ 销毁 ObservableImpl * obs.dispose() */ export class ObservableImpl> implements IObservable { /** Observable 状态对象,深层 Proxy */ public readonly state: TObservableState /** 全量订阅函数集合 */ private listeners: Set> = new Set() /** 字段订阅函数集合 */ private keyListeners: Map> = new Map() /** 待通知的字段集合 */ private pendingKeys: Set = new Set() /** 是否已经安排通知 */ private notifyScheduled = false /** 是否已销毁 */ private disposed = false constructor(initialState: TNonFunctionProperties) { // 创建深层响应式 Proxy this.state = this.makeReactive(initialState) as TObservableState } /** 创建深层 Proxy,拦截 get/set */ private makeReactive(obj: TNonFunctionProperties): TObservableState { const handler: ProxyHandler = { get: (target, prop: string | symbol, receiver) => { const key = prop as keyof T const value = Reflect.get(target, key, receiver) if (Array.isArray(value)) return this.wrapArray(value, key) if (typeof value === 'object' && value !== null) return this.makeReactive(value) return value }, set: (target, prop: string | symbol, value, receiver) => { const key = prop as keyof T const oldValue = target[key] if (oldValue !== value) { target[key] = value this.pendingKeys.add(key) this.scheduleNotify() } return true } } return new Proxy(obj, handler) as TObservableState } /** 包装数组方法,使 push/pop 等触发通知 */ private wrapArray(arr: any[], parentKey: keyof T): any { const self = this const arrayMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'] as const arrayMethods.forEach(method => { const original = arr[method] Object.defineProperty(arr, method, { value: function (...args: any[]) { const result = original.apply(this, args) self.pendingKeys.add(parentKey) self.scheduleNotify() return result }, writable: true, configurable: true, }) }) return arr } /** 安排下一次通知 */ private scheduleNotify(): void { if (!this.notifyScheduled && !this.disposed) { this.notifyScheduled = true Promise.resolve().then(() => this.flushNotify()) } } /** 执行通知 */ private flushNotify(): void { if (this.disposed) return const keys = Array.from(this.pendingKeys) this.pendingKeys.clear() this.notifyScheduled = false // 全量订阅 for (const fn of this.listeners) { fn(this.state) } // 字段订阅 const fnMap = new Map() for (const key of keys) { const set = this.keyListeners.get(key) if (!set) continue for (const fn of set) { if (!fnMap.has(fn)) fnMap.set(fn, []) fnMap.get(fn)!.push(key) } } fnMap.forEach((subKeys, fn) => { const result = {} as Pick subKeys.forEach(k => (result[k] = this.state[k])) fn(result) }) } /** 订阅整个状态变化 */ subscribe(fn: TObservableListener, options: { immediate?: boolean } = {}): () => void { this.listeners.add(fn) if (options.immediate) fn(this.state) return () => { this.listeners.delete(fn) } } /** 订阅指定字段变化 */ subscribeKey( keys: K | K[], fn: TObservableKeyListener, options: { immediate?: boolean } = {} ): () => void { const keyArray = Array.isArray(keys) ? keys : [keys] for (const key of keyArray) { if (!this.keyListeners.has(key)) this.keyListeners.set(key, new Set()) this.keyListeners.get(key)!.add(fn) } if (options.immediate) { const result = {} as Pick keyArray.forEach(k => (result[k] = this.state[k])) fn(result) } return () => { for (const key of keyArray) { this.keyListeners.get(key)?.delete(fn) } } } /** 批量更新状态 */ patch(values: Partial): void { for (const key in values) { if (Object.prototype.hasOwnProperty.call(values, key)) { const typedKey = key as keyof T this.state[typedKey] = values[typedKey]! this.pendingKeys.add(typedKey) } } this.scheduleNotify() } /** 销毁 Observable 实例 */ dispose(): void { this.disposed = true this.listeners.clear() this.keyListeners.clear() this.pendingKeys.clear() } /** 语法糖:返回一个可解构赋值的 Proxy */ toRefsProxy(): { [K in keyof T]: T[K] } { const self = this return new Proxy({} as T, { get(_, prop: string | symbol) { const key = prop as keyof T return self.state[key] }, set(_, prop: string | symbol, value) { const key = prop as keyof T self.state[key] = value return true }, ownKeys() { return Reflect.ownKeys(self.state) }, getOwnPropertyDescriptor(_, prop: string | symbol) { return { enumerable: true, configurable: true } } }) } }