diff options
| author | ruki <waruqi@gmail.com> | 2018-11-08 00:38:48 +0800 |
|---|---|---|
| committer | ruki <waruqi@gmail.com> | 2018-11-07 21:53:09 +0800 |
| commit | 26105034da4fcce7ac883c899d781f016559310d (patch) | |
| tree | c459a5dc4e3aa0972d9919033ece511ce76dd129 /node_modules/vue/src/core/observer/watcher.js | |
| parent | 2c77f00f1a7ecb6c8192f9c16d3b2001b254a107 (diff) | |
| download | xmake-docs-26105034da4fcce7ac883c899d781f016559310d.tar.gz xmake-docs-26105034da4fcce7ac883c899d781f016559310d.zip | |
switch to vuepress
Diffstat (limited to 'node_modules/vue/src/core/observer/watcher.js')
| -rw-r--r-- | node_modules/vue/src/core/observer/watcher.js | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/node_modules/vue/src/core/observer/watcher.js b/node_modules/vue/src/core/observer/watcher.js new file mode 100644 index 00000000..48a2e612 --- /dev/null +++ b/node_modules/vue/src/core/observer/watcher.js @@ -0,0 +1,240 @@ +/* @flow */ + +import { + warn, + remove, + isObject, + parsePath, + _Set as Set, + handleError +} from '../util/index' + +import { traverse } from './traverse' +import { queueWatcher } from './scheduler' +import Dep, { pushTarget, popTarget } from './dep' + +import type { SimpleSet } from '../util/index' + +let uid = 0 + +/** + * A watcher parses an expression, collects dependencies, + * and fires callback when the expression value changes. + * This is used for both the $watch() api and directives. + */ +export default class Watcher { + vm: Component; + expression: string; + cb: Function; + id: number; + deep: boolean; + user: boolean; + lazy: boolean; + sync: boolean; + dirty: boolean; + active: boolean; + deps: Array<Dep>; + newDeps: Array<Dep>; + depIds: SimpleSet; + newDepIds: SimpleSet; + getter: Function; + value: any; + + constructor ( + vm: Component, + expOrFn: string | Function, + cb: Function, + options?: ?Object, + isRenderWatcher?: boolean + ) { + this.vm = vm + if (isRenderWatcher) { + vm._watcher = this + } + vm._watchers.push(this) + // options + if (options) { + this.deep = !!options.deep + this.user = !!options.user + this.lazy = !!options.lazy + this.sync = !!options.sync + } else { + this.deep = this.user = this.lazy = this.sync = false + } + this.cb = cb + this.id = ++uid // uid for batching + this.active = true + this.dirty = this.lazy // for lazy watchers + this.deps = [] + this.newDeps = [] + this.depIds = new Set() + this.newDepIds = new Set() + this.expression = process.env.NODE_ENV !== 'production' + ? expOrFn.toString() + : '' + // parse expression for getter + if (typeof expOrFn === 'function') { + this.getter = expOrFn + } else { + this.getter = parsePath(expOrFn) + if (!this.getter) { + this.getter = function () {} + process.env.NODE_ENV !== 'production' && warn( + `Failed watching path: "${expOrFn}" ` + + 'Watcher only accepts simple dot-delimited paths. ' + + 'For full control, use a function instead.', + vm + ) + } + } + this.value = this.lazy + ? undefined + : this.get() + } + + /** + * Evaluate the getter, and re-collect dependencies. + */ + get () { + pushTarget(this) + let value + const vm = this.vm + try { + value = this.getter.call(vm, vm) + } catch (e) { + if (this.user) { + handleError(e, vm, `getter for watcher "${this.expression}"`) + } else { + throw e + } + } finally { + // "touch" every property so they are all tracked as + // dependencies for deep watching + if (this.deep) { + traverse(value) + } + popTarget() + this.cleanupDeps() + } + return value + } + + /** + * Add a dependency to this directive. + */ + addDep (dep: Dep) { + 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 + } + + /** + * Subscriber interface. + * Will be called when a dependency changes. + */ + update () { + /* istanbul ignore else */ + if (this.lazy) { + this.dirty = true + } else if (this.sync) { + this.run() + } else { + queueWatcher(this) + } + } + + /** + * Scheduler job interface. + * Will be called by the scheduler. + */ + run () { + if (this.active) { + const value = this.get() + if ( + value !== this.value || + // Deep watchers and watchers on Object/Arrays should fire even + // when the value is the same, because the value may + // have mutated. + isObject(value) || + this.deep + ) { + // set new value + const oldValue = this.value + this.value = value + if (this.user) { + try { + this.cb.call(this.vm, value, oldValue) + } catch (e) { + handleError(e, this.vm, `callback for watcher "${this.expression}"`) + } + } else { + this.cb.call(this.vm, value, oldValue) + } + } + } + } + + /** + * Evaluate the value of the watcher. + * This only gets called for lazy watchers. + */ + evaluate () { + this.value = this.get() + this.dirty = false + } + + /** + * Depend on all deps collected by this watcher. + */ + depend () { + let i = this.deps.length + while (i--) { + this.deps[i].depend() + } + } + + /** + * Remove self from all dependencies' subscriber list. + */ + teardown () { + if (this.active) { + // remove self from vm's watcher list + // this is a somewhat expensive operation so we skip it + // if the vm is being destroyed. + if (!this.vm._isBeingDestroyed) { + remove(this.vm._watchers, this) + } + let i = this.deps.length + while (i--) { + this.deps[i].removeSub(this) + } + this.active = false + } + } +} |
