import config from '../config' import { DebuggerOptions, DebuggerEventExtraInfo } from 'v3' let uid = 0 const pendingCleanupDeps: Dep[] = [] export const cleanupDeps = () => { for (let i = 0; i < pendingCleanupDeps.length; i++) { const dep = pendingCleanupDeps[i] dep.subs = dep.subs.filter(s => s) dep._pending = false } pendingCleanupDeps.length = 0 } /** * @internal */ export interface DepTarget extends DebuggerOptions { id: number addDep(dep: Dep): void update(): void } /** * A dep is an observable that can have multiple * directives subscribing to it. * @internal */ export default class Dep { static target?: DepTarget | null id: number subs: Array // pending subs cleanup _pending = false constructor() { this.id = uid++ this.subs = [] } addSub(sub: DepTarget) { this.subs.push(sub) } removeSub(sub: DepTarget) { // #12696 deps with massive amount of subscribers are extremely slow to // clean up in Chromium // to workaround this, we unset the sub for now, and clear them on // next scheduler flush. this.subs[this.subs.indexOf(sub)] = null if (!this._pending) { this._pending = true pendingCleanupDeps.push(this) } } depend(info?: DebuggerEventExtraInfo) { if (Dep.target) { Dep.target.addDep(this) if (__DEV__ && info && Dep.target.onTrack) { Dep.target.onTrack({ effect: Dep.target, ...info }) } } } notify(info?: DebuggerEventExtraInfo) { // stabilize the subscriber list first const subs = this.subs.filter(s => s) as DepTarget[] if (__DEV__ && !config.async) { // subs aren't sorted in scheduler if not running async // we need to sort them now to make sure they fire in correct // order subs.sort((a, b) => a.id - b.id) } for (let i = 0, l = subs.length; i < l; i++) { const sub = subs[i] if (__DEV__ && info) { sub.onTrigger && sub.onTrigger({ effect: subs[i], ...info }) } sub.update() } } } // The current target watcher being evaluated. // This is globally unique because only one watcher // can be evaluated at a time. Dep.target = null const targetStack: Array = [] export function pushTarget(target?: DepTarget | null) { targetStack.push(target) Dep.target = target } export function popTarget() { targetStack.pop() Dep.target = targetStack[targetStack.length - 1] }