|
|
@@ -32,206 +32,184 @@ let prevTarget
|
|
|
* - {Function} [postProcess]
|
|
|
* @constructor
|
|
|
*/
|
|
|
-
|
|
|
-export default function Watcher (vm, expOrFn, cb, options) {
|
|
|
- // mix in options
|
|
|
- if (options) {
|
|
|
- extend(this, options)
|
|
|
- }
|
|
|
- const isFn = typeof expOrFn === 'function'
|
|
|
- this.vm = vm
|
|
|
- vm._watchers.push(this)
|
|
|
- this.expression = expOrFn
|
|
|
- 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()
|
|
|
- // parse expression for getter
|
|
|
- if (isFn) {
|
|
|
- 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
|
|
|
- )
|
|
|
+export default class Watcher {
|
|
|
+ constructor (vm, expOrFn, cb, options) {
|
|
|
+ // mix in options
|
|
|
+ if (options) {
|
|
|
+ extend(this, options)
|
|
|
+ }
|
|
|
+ const isFn = typeof expOrFn === 'function'
|
|
|
+ this.vm = vm
|
|
|
+ vm._watchers.push(this)
|
|
|
+ this.expression = expOrFn
|
|
|
+ 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()
|
|
|
+ // parse expression for getter
|
|
|
+ if (isFn) {
|
|
|
+ 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()
|
|
|
}
|
|
|
- this.value = this.lazy
|
|
|
- ? undefined
|
|
|
- : this.get()
|
|
|
- // state for avoiding false triggers for deep and Array
|
|
|
- // watchers during vm._digest()
|
|
|
- this.queued = this.shallow = false
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * Evaluate the getter, and re-collect dependencies.
|
|
|
- */
|
|
|
|
|
|
-Watcher.prototype.get = function () {
|
|
|
- this.beforeGet()
|
|
|
- const value = this.getter.call(this.vm, this.vm)
|
|
|
- // "touch" every property so they are all tracked as
|
|
|
- // dependencies for deep watching
|
|
|
- if (this.deep) {
|
|
|
- traverse(value)
|
|
|
+ /**
|
|
|
+ * Evaluate the getter, and re-collect dependencies.
|
|
|
+ */
|
|
|
+ get () {
|
|
|
+ this.beforeGet()
|
|
|
+ const value = this.getter.call(this.vm, this.vm)
|
|
|
+ // "touch" every property so they are all tracked as
|
|
|
+ // dependencies for deep watching
|
|
|
+ if (this.deep) {
|
|
|
+ traverse(value)
|
|
|
+ }
|
|
|
+ this.afterGet()
|
|
|
+ return value
|
|
|
}
|
|
|
- this.afterGet()
|
|
|
- return value
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * Prepare for dependency collection.
|
|
|
- */
|
|
|
-
|
|
|
-Watcher.prototype.beforeGet = function () {
|
|
|
- prevTarget = Dep.target
|
|
|
- Dep.target = this
|
|
|
-}
|
|
|
|
|
|
-/**
|
|
|
- * Add a dependency to this directive.
|
|
|
- *
|
|
|
- * @param {Dep} dep
|
|
|
- */
|
|
|
+ /**
|
|
|
+ * Prepare for dependency collection.
|
|
|
+ */
|
|
|
+ beforeGet () {
|
|
|
+ prevTarget = Dep.target
|
|
|
+ Dep.target = this
|
|
|
+ }
|
|
|
|
|
|
-Watcher.prototype.addDep = function (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)
|
|
|
+ /**
|
|
|
+ * Add a dependency to this directive.
|
|
|
+ *
|
|
|
+ * @param {Dep} dep
|
|
|
+ */
|
|
|
+ addDep (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.
|
|
|
- */
|
|
|
|
|
|
-Watcher.prototype.afterGet = function () {
|
|
|
- Dep.target = prevTarget
|
|
|
- let i = this.deps.length
|
|
|
- while (i--) {
|
|
|
- const dep = this.deps[i]
|
|
|
- if (!this.newDepIds.has(dep.id)) {
|
|
|
- dep.removeSub(this)
|
|
|
+ /**
|
|
|
+ * Clean up for dependency collection.
|
|
|
+ */
|
|
|
+ afterGet () {
|
|
|
+ Dep.target = prevTarget
|
|
|
+ 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
|
|
|
}
|
|
|
- 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.
|
|
|
- *
|
|
|
- * @param {Boolean} shallow
|
|
|
- */
|
|
|
|
|
|
-Watcher.prototype.update = function (shallow) {
|
|
|
- if (this.lazy) {
|
|
|
- this.dirty = true
|
|
|
- } else if (this.sync) {
|
|
|
- this.run()
|
|
|
- } else {
|
|
|
- // if queued, only overwrite shallow with non-shallow,
|
|
|
- // but not the other way around.
|
|
|
- this.shallow = this.queued
|
|
|
- ? shallow
|
|
|
- ? this.shallow
|
|
|
- : false
|
|
|
- : !!shallow
|
|
|
- this.queued = true
|
|
|
- pushWatcher(this)
|
|
|
+ /**
|
|
|
+ * Subscriber interface.
|
|
|
+ * Will be called when a dependency changes.
|
|
|
+ */
|
|
|
+ update () {
|
|
|
+ if (this.lazy) {
|
|
|
+ this.dirty = true
|
|
|
+ } else if (this.sync) {
|
|
|
+ this.run()
|
|
|
+ } else {
|
|
|
+ pushWatcher(this)
|
|
|
+ }
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * Batcher job interface.
|
|
|
- * Will be called by the batcher.
|
|
|
- */
|
|
|
|
|
|
-Watcher.prototype.run = function () {
|
|
|
- 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; but only do so if this is a
|
|
|
- // non-shallow update (caused by a vm digest).
|
|
|
- ((isObject(value) || this.deep) && !this.shallow)
|
|
|
- ) {
|
|
|
- // set new value
|
|
|
- const oldValue = this.value
|
|
|
- this.value = value
|
|
|
- this.cb.call(this.vm, value, oldValue)
|
|
|
+ /**
|
|
|
+ * Batcher job interface.
|
|
|
+ * Will be called by the batcher.
|
|
|
+ */
|
|
|
+ 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
|
|
|
+ this.cb.call(this.vm, value, oldValue)
|
|
|
+ }
|
|
|
}
|
|
|
- this.queued = this.shallow = false
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * Evaluate the value of the watcher.
|
|
|
- * This only gets called for lazy watchers.
|
|
|
- */
|
|
|
-
|
|
|
-Watcher.prototype.evaluate = function () {
|
|
|
- // avoid overwriting another watcher that is being
|
|
|
- // collected.
|
|
|
- const current = Dep.target
|
|
|
- this.value = this.get()
|
|
|
- this.dirty = false
|
|
|
- Dep.target = current
|
|
|
-}
|
|
|
|
|
|
-/**
|
|
|
- * Depend on all deps collected by this watcher.
|
|
|
- */
|
|
|
-
|
|
|
-Watcher.prototype.depend = function () {
|
|
|
- let i = this.deps.length
|
|
|
- while (i--) {
|
|
|
- this.deps[i].depend()
|
|
|
+ /**
|
|
|
+ * Evaluate the value of the watcher.
|
|
|
+ * This only gets called for lazy watchers.
|
|
|
+ */
|
|
|
+ evaluate () {
|
|
|
+ // avoid overwriting another watcher that is being
|
|
|
+ // collected.
|
|
|
+ const current = Dep.target
|
|
|
+ this.value = this.get()
|
|
|
+ this.dirty = false
|
|
|
+ Dep.target = current
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * Remove self from all dependencies' subcriber list.
|
|
|
- */
|
|
|
|
|
|
-Watcher.prototype.teardown = function () {
|
|
|
- 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 or is performing a v-for
|
|
|
- // re-render (the watcher list is then filtered by v-for).
|
|
|
- if (!this.vm._isBeingDestroyed && !this.vm._vForRemoving) {
|
|
|
- remove(this.vm._watchers, this)
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Depend on all deps collected by this watcher.
|
|
|
+ */
|
|
|
+ depend () {
|
|
|
let i = this.deps.length
|
|
|
while (i--) {
|
|
|
- this.deps[i].removeSub(this)
|
|
|
+ this.deps[i].depend()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Remove self from all dependencies' subcriber 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 or is performing a v-for
|
|
|
+ // re-render (the watcher list is then filtered by v-for).
|
|
|
+ if (!this.vm._isBeingDestroyed && !this.vm._vForRemoving) {
|
|
|
+ remove(this.vm._watchers, this)
|
|
|
+ }
|
|
|
+ let i = this.deps.length
|
|
|
+ while (i--) {
|
|
|
+ this.deps[i].removeSub(this)
|
|
|
+ }
|
|
|
+ this.active = false
|
|
|
+ this.vm = this.cb = this.value = null
|
|
|
}
|
|
|
- this.active = false
|
|
|
- this.vm = this.cb = this.value = null
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -243,7 +221,6 @@ Watcher.prototype.teardown = function () {
|
|
|
* @param {*} val
|
|
|
* @param {Set} seen
|
|
|
*/
|
|
|
-
|
|
|
const seenObjects = new Set()
|
|
|
function traverse (val, seen) {
|
|
|
let i, keys
|