aboutsummaryrefslogtreecommitdiff
path: root/node_modules/vue/src/core/observer/watcher.js
diff options
context:
space:
mode:
authorruki <waruqi@gmail.com>2018-11-08 00:38:48 +0800
committerruki <waruqi@gmail.com>2018-11-07 21:53:09 +0800
commit26105034da4fcce7ac883c899d781f016559310d (patch)
treec459a5dc4e3aa0972d9919033ece511ce76dd129 /node_modules/vue/src/core/observer/watcher.js
parent2c77f00f1a7ecb6c8192f9c16d3b2001b254a107 (diff)
downloadxmake-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.js240
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
+ }
+ }
+}