UNPKG

@mpxjs/core

Version:

mpx runtime core

135 lines (120 loc) 2.96 kB
import Dep, { pushTarget, popTarget } from './dep' import { recordEffectScope } from './effectScope' import { PausedState } from '../helper/const' let uid = 0 let shouldTrack = true const trackStack = [] export function pauseTracking () { trackStack.push(shouldTrack) shouldTrack = false } export function resetTracking () { const last = trackStack.pop() shouldTrack = last === undefined ? true : last } export class ReactiveEffect { active = true deps = [] newDeps = [] depIds = new Set() newDepIds = new Set() allowRecurse = false constructor ( fn, scheduler, scope ) { this.id = ++uid this.fn = fn this.scheduler = scheduler this.pausedState = PausedState.resumed recordEffectScope(this, scope) } // run fn and return value run () { if (!this.active) return this.fn() const lastShouldTrack = shouldTrack try { pushTarget(this) shouldTrack = true return this.fn() } finally { popTarget() shouldTrack = lastShouldTrack this.deferStop ? this.stop() : this.cleanupDeps() } } // add dependency to this addDep (dep) { if (!shouldTrack) return const id = dep.id if (!this.newDepIds.has(id)) { this.newDepIds.add(id) this.newDeps.push(dep) if (!this.depIds.has(id)) { dep.addSub(this) } } } // Clean up for dependency collection. cleanupDeps () { let i = this.deps.length while (i--) { const dep = this.deps[i] if (!this.newDepIds.has(dep.id)) { dep.removeSub(this) } } let tmp = this.depIds this.depIds = this.newDepIds this.newDepIds = tmp this.newDepIds.clear() tmp = this.deps this.deps = this.newDeps this.newDeps = tmp this.newDeps.length = 0 } // same as trigger update () { // avoid dead cycle if (Dep.target !== this || this.allowRecurse) { if (this.pausedState !== PausedState.resumed) { this.pausedState = PausedState.dirty } else { this.scheduler ? this.scheduler() : this.run() } } } // pass through deps for computed depend () { let i = this.deps.length while (i--) { this.deps[i].depend() } } // Remove self from all dependencies' subscriber list. stop () { if (Dep.target === this) { this.deferStop = true } else if (this.active) { let i = this.deps.length while (i--) { this.deps[i].removeSub(this) } typeof this.onStop === 'function' && this.onStop() this.active = false } } pause () { if (this.pausedState !== PausedState.dirty) { this.pausedState = PausedState.paused } } resume (ignoreDirty = false) { const lastPausedState = this.pausedState this.pausedState = PausedState.resumed if (!ignoreDirty && lastPausedState === PausedState.dirty) { this.scheduler ? this.scheduler() : this.run() } } }