| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 | 
							- /* @flow */
 
- import {
 
-   warn,
 
-   remove,
 
-   isObject,
 
-   parsePath,
 
-   _Set as Set,
 
-   handleError,
 
-   noop
 
- } 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;
 
-   before: ?Function;
 
-   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
 
-       this.before = options.before
 
-     } 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 = noop
 
-         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
 
-     }
 
-   }
 
- }
 
 
  |