import type { IObservable, TNonFunctionProperties, TObservableKeyListener, TObservableListener, TObservableState, } from '@/core/state/IObservable.ts' /** * 创建一个可观察对象,用于管理状态和事件。 * WeakRef 和垃圾回收功能 * @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 ObservableWeakRefImpl> implements IObservable { /** ObservableImpl 的状态对象,深层 Proxy */ public readonly state: TObservableState /** 全量订阅列表 */ private listeners: Set> | TObservableListener> = new Set() /** 字段订阅列表 */ private keyListeners: Map | Function>> = new Map() /** FinalizationRegistry 用于自动清理 WeakRef */ private registry?: FinalizationRegistry> /** 待通知的字段 */ private pendingKeys: Set = new Set() /** 通知调度状态 */ private notifyScheduled = false /** 已销毁标记 */ private disposed = false constructor(initialState: TNonFunctionProperties) { if (typeof WeakRef !== 'undefined' && typeof FinalizationRegistry !== 'undefined') { this.registry = new FinalizationRegistry((ref: WeakRef) => { this.listeners.delete(ref as unknown as TObservableListener) this.keyListeners.forEach(set => set.delete(ref)) }) } // 创建深层响应式 Proxy this.state = this.makeReactive(initialState) as TObservableState } /** 创建响应式对象,深层递归 Proxy + 数组方法拦截 */ 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/splice 等触发通知 */ 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 ref of this.listeners) { const fn = this.deref(ref) if (fn) fn(this.state) else this.listeners.delete(ref as TObservableListener) } // 字段订阅 const fnMap = new Map() for (const key of keys) { const set = this.keyListeners.get(key) if (!set) continue for (const ref of set) { const fn = this.deref(ref) if (!fn) { set.delete(ref) continue } 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 { const ref = this.makeRef(fn) this.listeners.add(ref) this.registry?.register(fn, ref as WeakRef) if (options.immediate) fn(this.state) return () => { this.listeners.delete(ref) this.registry?.unregister(fn) } } /** 字段订阅 */ subscribeKey( keys: K | K[], fn: TObservableKeyListener, options: { immediate?: boolean } = {} ): () => void { const keyArray = Array.isArray(keys) ? keys : [keys] const refs: (WeakRef | Function)[] = [] for (const key of keyArray) { if (!this.keyListeners.has(key)) this.keyListeners.set(key, new Set()) const ref = this.makeRef(fn) this.keyListeners.get(key)!.add(ref) this.registry?.register(fn as unknown as Function, ref as WeakRef) refs.push(ref) } if (options.immediate) { const result = {} as Pick keyArray.forEach(k => (result[k] = this.state[k])) fn(result) } return () => { for (let i = 0; i < keyArray.length; i++) { const set = this.keyListeners.get(keyArray[i]) if (set) set.delete(refs[i]) } this.registry?.unregister(fn as unknown as Function) } } /** 批量更新 */ 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() } /** 销毁 ObservableImpl */ dispose(): void { this.disposed = true this.listeners.clear() this.keyListeners.clear() this.pendingKeys.clear() } /** 语法糖:解构赋值直接赋值触发通知 */ 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 } } }) } /** WeakRef 创建 */ private makeRef(fn: F): WeakRef | F { return typeof WeakRef !== 'undefined' ? new WeakRef(fn) : fn } /** WeakRef 解引用 */ private deref(ref: WeakRef | F): F | undefined { return typeof WeakRef !== 'undefined' && ref instanceof WeakRef ? ref.deref() : (ref as F) } }