PHP Classes

File: public/js/lib/vue/src/watcher.js

Recommend this page to a friend!
  Classes of Sergey Beskorovayniy   Silex MVC Blog   public/js/lib/vue/src/watcher.js   Download  
File: public/js/lib/vue/src/watcher.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Silex MVC Blog
MVC based blog using on the Silex micro-framework
Author: By
Last change:
Date: 8 years ago
Size: 8,748 bytes
 

Contents

Class file image Download
import config from './config' import Dep from './observer/dep' import { parseExpression } from './parsers/expression' import { pushWatcher } from './batcher' import { extend, warn, isArray, isObject, nextTick, _Set as Set } 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. * * @param {Vue} vm * @param {String|Function} expOrFn * @param {Function} cb * @param {Object} options * - {Array} filters * - {Boolean} twoWay * - {Boolean} deep * - {Boolean} user * - {Boolean} sync * - {Boolean} lazy * - {Function} [preProcess] * - {Function} [postProcess] * @constructor */ export default function Watcher (vm, expOrFn, cb, options) { // mix in options if (options) { extend(this, options) } var 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() this.prevError = null // for async error stacks // parse expression for getter/setter if (isFn) { this.getter = expOrFn this.setter = undefined } else { var res = parseExpression(expOrFn, this.twoWay) this.getter = res.get this.setter = res.set } 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() var scope = this.scope || this.vm var value try { value = this.getter.call(scope, scope) } catch (e) { if ( process.env.NODE_ENV !== 'production' && config.warnExpressionErrors ) { warn( 'Error when evaluating expression ' + '"' + this.expression + '": ' + e.toString(), this.vm ) } } // "touch" every property so they are all tracked as // dependencies for deep watching if (this.deep) { traverse(value) } if (this.preProcess) { value = this.preProcess(value) } if (this.filters) { value = scope._applyFilters(value, null, this.filters, false) } if (this.postProcess) { value = this.postProcess(value) } this.afterGet() return value } /** * Set the corresponding value with the setter. * * @param {*} value */ Watcher.prototype.set = function (value) { var scope = this.scope || this.vm if (this.filters) { value = scope._applyFilters( value, this.value, this.filters, true) } try { this.setter.call(scope, scope, value) } catch (e) { if ( process.env.NODE_ENV !== 'production' && config.warnExpressionErrors ) { warn( 'Error when evaluating setter ' + '"' + this.expression + '": ' + e.toString(), this.vm ) } } // two-way sync for v-for alias var forContext = scope.$forContext if (forContext && forContext.alias === this.expression) { if (forContext.filters) { process.env.NODE_ENV !== 'production' && warn( 'It seems you are using two-way binding on ' + 'a v-for alias (' + this.expression + '), and the ' + 'v-for has filters. This will not work properly. ' + 'Either remove the filters or use an array of ' + 'objects and bind to object properties instead.', this.vm ) return } forContext._withLock(function () { if (scope.$key) { // original is an object forContext.rawValue[scope.$key] = value } else { forContext.rawValue.$set(scope.$index, value) } }) } } /** * Prepare for dependency collection. */ Watcher.prototype.beforeGet = function () { Dep.target = this } /** * Add a dependency to this directive. * * @param {Dep} dep */ Watcher.prototype.addDep = function (dep) { var 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 = null var i = this.deps.length while (i--) { var dep = this.deps[i] if (!this.newDepIds.has(dep.id)) { dep.removeSub(this) } } var 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 || !config.async) { 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 // record before-push error stack in debug mode /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.debug) { this.prevError = new Error('[vue] async stack trace') } pushWatcher(this) } } /** * Batcher job interface. * Will be called by the batcher. */ Watcher.prototype.run = function () { if (this.active) { var 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 var oldValue = this.value this.value = value // in debug + async mode, when a watcher callbacks // throws, we also throw the saved before-push error // so the full cross-tick stack trace is available. var prevError = this.prevError /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.debug && prevError) { this.prevError = null try { this.cb.call(this.vm, value, oldValue) } catch (e) { nextTick(function () { throw prevError }, 0) throw e } } else { 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. var 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 () { var i = this.deps.length while (i--) { this.deps[i].depend() } } /** * 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) { this.vm._watchers.$remove(this) } var i = this.deps.length while (i--) { this.deps[i].removeSub(this) } this.active = false this.vm = this.cb = this.value = null } } /** * Recrusively traverse an object to evoke all converted * getters, so that every nested property inside the object * is collected as a "deep" dependency. * * @param {*} val */ const seenObjects = new Set() function traverse (val, seen) { let i, keys if (!seen) { seen = seenObjects seen.clear() } const isA = isArray(val) const isO = isObject(val) if ((isA || isO) && Object.isExtensible(val)) { if (val.__ob__) { var depId = val.__ob__.dep.id if (seen.has(depId)) { return } else { seen.add(depId) } } if (isA) { i = val.length while (i--) traverse(val[i], seen) } else if (isO) { keys = Object.keys(val) i = keys.length while (i--) traverse(val[keys[i]], seen) } } }