/* * proxy.ts * Copyright (C) 2024 veypi * 2024-10-23 17:20 * Distributed under terms of the GPL license. */ type voidFn = () => void // TODO: 没有删除机制 const callbackCache: voidFn[] = [] const cacheUpdateList: number[] = [] // 界面响应频率40hz setInterval(() => { let list = new Set(cacheUpdateList.splice(0)) for (let l of list) { callbackCache[l]() } }, 25) function generateUniqueId() { const timestamp = performance.now().toString(36); const random = Math.random().toString(36).substring(2, 5); return `${timestamp}-${random}`; } function ForceUpdate() { for (let c of callbackCache) { c() } } var listen_tags: number[] = [] function Listen(callback: voidFn) { listen_tags.push(callbackCache.length) callbackCache.push(callback) callback() listen_tags.pop() } const isProxy = Symbol("isProxy") const DataID = Symbol("DataID") function Watch(data: T) { const did = generateUniqueId() let isArray = false if (Object.prototype.toString.call(data) === '[object Array]') { isArray = true } // console.log(`watch ${did} ${isArray}`, data) const listeners: { [key: string | symbol]: number[] } = {} const handler = { get(target: Object, key: string | symbol, receiver: any) { if (key === isProxy) { return true } if (key === DataID) { return did } const value = Reflect.get(target, key, receiver) if (typeof value === 'object' && value !== null) { if (value[isProxy]) { return value } else { let newValue = Watch(value) Reflect.set(target, key, newValue, receiver) return newValue } } let idx = -1 if (listen_tags.length > 0) { let lkey = key idx = listen_tags[listen_tags.length - 1] if (isArray) { lkey = '' } if (!listeners[lkey]) { listeners[lkey] = [idx] } else if (listeners[lkey].indexOf(idx) == -1) { listeners[lkey].push(idx) } } // console.log(`${did} get ${key.toString()}:${value} ${idx}`) return value; }, set(target: Object, key: string | symbol, newValue: any, receiver: any) { // console.log(`${did} set ${key.toString()} ${newValue}`) const result = Reflect.set(target, key, newValue, receiver); if (result) { let lkey = key if (isArray) { lkey = '' } if (listeners[lkey]) { for (let cb of listeners[lkey]) { cacheUpdateList.push(cb) } } } return result; }, deleteProperty(target: Object, key: string) { // console.log(`del ${key}`) const result = Reflect.deleteProperty(target, key); if (result) { } return result } }; let res = new Proxy(data, handler); // Symbol(Symbol.toStringTag) // res[Symbol.toStringTag] = 'Proxy' return res } export default { Watch, Listen, ForceUpdate, DataID, generateUniqueId }