import { arrayEqual, clearImmediate, isFunction, isUndefined, registerEvent, setImmediate, } from '../util'; import type { PropertyPathItem, ViewModel } from '../vm'; import { VM_RAW, VM_WATCHER_VALUE, getValueByPath, innerWatchPath } from '../vm'; import { type ComponentHost, addUnmountFn } from './component'; ////// 这个文件里的函数都是用于给编译器转译 tsx 时使用的 Component 的 watch 函数。 ///// ////// 业务中请直接使用 `watch` 函数进行 Component 或 ViewModel 的监听。 ///// export function watchForRender( watcher: Pick< ViewWatcher, typeof VM_WATCHER_DESTROY | typeof VM_WATCHER_PARENT | typeof VM_WATCHER_VALUE >, renderFn: (v: unknown) => void, hostComponent: ComponentHost, ) { watcher[VM_WATCHER_PARENT] = { [VM_WATCHER_NOTIFY]: renderFn, }; renderFn(watcher[VM_WATCHER_VALUE]); addUnmountFn(hostComponent, () => watcher[VM_WATCHER_DESTROY]()); } // eslint-disable-next-line @typescript-eslint/no-explicit-any function wrapEventBind($ele: any, eventName: any, capture: boolean) { let dereg: (() => void) | undefined = undefined; return (handler: unknown) => { if (dereg) { dereg(); dereg = undefined; } if (isFunction(handler)) { dereg = registerEvent($ele, eventName, handler, capture); } }; } /** * 给编译器使用的 dom 元素的事件绑定器。会记住上一次绑定的事件,再次绑定时先解绑上一次绑定的。 * 用于当 dom 元素的事件属性的值是非函数时,搭配 ExprWatcher 等进行动态绑定。 */ export function watchForDOMEventBind( watcher: Pick< ViewWatcher, typeof VM_WATCHER_DESTROY | typeof VM_WATCHER_PARENT | typeof VM_WATCHER_VALUE >, $ele: Element | Window | Document, eventName: string, capture: boolean, hostComponent: ComponentHost, ) { const bindEvent = wrapEventBind($ele, eventName, capture); watcher[VM_WATCHER_PARENT] = { [VM_WATCHER_NOTIFY]: bindEvent, }; bindEvent(watcher[VM_WATCHER_VALUE]); addUnmountFn(hostComponent, () => { bindEvent(undefined); // 通过 bind undefined 来销毁旧的监听。 watcher[VM_WATCHER_DESTROY](); }); } /** * watchForDOMEventBind 的简化版本,用于最简单的诸如 {props.a.b.c} 这样的纯 path 的表达式事件属性的监控。 */ export function watchPathForDOMEventBind( target: ViewModel, path: PropertyPathItem[], $ele: Element | Window | Document, eventName: string, capture: boolean, hostComponent: ComponentHost, ) { if (!target) { return; } const bindEvent = wrapEventBind($ele, eventName, capture); const val = getValueByPath(target, path); bindEvent(val); if (isUndefined(target[VM_RAW])) { return; } const unwatch = innerWatchPath(target, val, bindEvent, path, true); addUnmountFn(hostComponent, () => { bindEvent(undefined); // 通过 bind undefined 来销毁旧的监听。 unwatch(); }); } /** * 用于最简单的诸如 {this.a.b.c} 这样的纯 path 的表达式的监控。同时也支持附带了简单 `!` 运算符的情况。 * @param notOp 0 代表不进行 bool 转换,1 代表一次(!运算,例如 `