The introduction

Vue source response principle part should be a more important part, whether in our study or job interview is a point of comparison, this article will focus on the induction of VUe2. X responsive source analysis

The source code section

The entry point to the responsive principle analyzed in the previous article is initState(VM)Copy the code

initState

src/core/instance/state.js

* Two things: * Data responsive entries: functions, methods, data, computed, watch The attributes of props, methods, data, and computed objects must be the same in the same priority order. * The key in computed cannot be the same as the key in props and data. */ export function initState (VM: Component) {vm._watchers = [] // Handle props and set the response for each attribute of the props, Const opts = vm.$options if (opts.props) initProps(vm, opts.props) // Handle the methods object, Check whether the value of each attribute is a function, props, and props. Vm [key] = methods[key] if (opts.methods) initMethods(vm, opts.methods) 3. Set a response for the data on the data object */ if (opts.data) {initData(vm) } else {observe(vm._data = {}, true /* asRootData */)} /** * * 1. Create a watcher instance for computed[key]. The default is lazy execution * 2. */ if (opts.computed) initComputed(VM, opts.computed) /** * Three things: Create an instance of watcher for each watch.key. Create a one-to-many watcher instance for each watcher. */ if (opts.watch && opts.watch! == nativeWatch) {initWatch(vm, opts.watch)}} // Handle props and set the response for each property of the props, Function initProps (vm: Component, propsOptions: Object) {const propsData = vm $options. PropsData | | {} const props. = the vm props _props = {} / / cache of each key, Cache prop keys so that future props updates can iterate using Array // Instead of dynamic object key enumeration. const keys = vm.$options._propKeys = [] const isRoot = ! vm.$parent // root instance props should be converted if (! {toggleObserving(false)} for (const key in propsOptions) {keys.push(key) = const value = validateProp(key, propsOptions, propsData, vm) /* istanbul ignore else */ if (process.env.NODE_ENV ! == 'production') { const hyphenatedKey = hyphenate(key) if (isReservedAttribute(hyphenatedKey) || config.isReservedAttr(hyphenatedKey)) { warn( `"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`, vm ) } defineReactive(props, key, value, () => { if (! isRoot && ! isUpdatingChildComponent) { warn( `Avoid mutating a prop directly since the value will be ` + `overwritten whenever the parent component re-renders. ` + `Instead, use a data or computed property based on the prop's ` + `value. Prop being mutated: "${key}"`, vm ) } }) } else { defineReactive(props, key, value) } // static props are already proxied on the component's prototype // during Vue.extend(). We only need to proxy props defined at // instantiation here. if (! PropsKey (vm, '_props', key)}} toggleObserving(true)}Copy the code

initProps

src/core/instance/state.js

Function initProps (vm: Component, propsOptions: Object) {const propsData = vm $options. PropsData | | {} const props. = the vm props _props = {} / / cache of each key, Cache prop keys so that future props updates can iterate using Array // Instead of dynamic object key enumeration. const keys = vm.$options._propKeys = [] const isRoot = ! vm.$parent // root instance props should be converted if (! {toggleObserving(false)} for (const key in propsOptions) {keys.push(key) = const value = validateProp(key, propsOptions, propsData, vm) /* istanbul ignore else */ if (process.env.NODE_ENV ! == 'production') { const hyphenatedKey = hyphenate(key) if (isReservedAttribute(hyphenatedKey) || config.isReservedAttr(hyphenatedKey)) { warn( `"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`, vm ) } defineReactive(props, key, value, () => { if (! isRoot && ! isUpdatingChildComponent) { warn( `Avoid mutating a prop directly since the value will be ` + `overwritten whenever the parent component re-renders. ` + `Instead, use a data or computed property based on the prop's ` + `value. Prop being mutated: "${key}"`, vm ) } }) } else { defineReactive(props, key, value) } // static props are already proxied on the component's prototype // during Vue.extend(). We only need to proxy props defined at // instantiation here. if (! PropsKey (vm, '_props', key)}} toggleObserving(true)}Copy the code

proxy

src/core/instance/state.js

/** * use object.defineProperty to prop, props,data */ export function proxy (target: Object, sourceKey: string, key: string) { sharedPropertyDefinition.get = function proxyGetter () { return this[sourceKey][key] } sharedPropertyDefinition.set = function proxySetter (val) { this[sourceKey][key] = val } Object.defineProperty(target, key, sharedPropertyDefinition) }Copy the code

initMethods

src/core/instance/state.js

* Did the following three things, but the most important was the third thing * 1, methoss[key], The key in the methods (props) must not be the same as the key in the methods (props). The key in methos overlaps with the existing method on the Vue instance. Vm [key] = methods[key] */ function initMethods (VM: Component, methods:) Object) { const props = vm.$options.props for (const key in methods) { if (process.env.NODE_ENV ! == 'production') { if (typeof methods[key] ! == 'function') { warn( `Method "${key}" has type "${typeof methods[key]}" in the component definition. ` + `Did you reference the function correctly? `, vm ) } if (props && hasOwn(props, key)) { warn( `Method "${key}" has already been defined as a prop.`, vm ) } if ((key in vm) && isReserved(key)) { warn( `Method "${key}" conflicts with an existing Vue instance method. ` + `Avoid defining component methods that start with _ or $.` ) } } vm[key] = typeof methods[key] ! == 'function' ? noop : bind(methods[key], vm) } }Copy the code

initData

src/core/instance/state.js

/** * did three things ** * Function initData (vm: functions, functions, functions, functions, functions, functions, functions, functions, functions, functions, functions, functions, functions, functions, functions, functions, functions, functions Component) { let data = vm.$options.data data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {} if (! isPlainObject(data)) { data = {} process.env.NODE_ENV ! == 'production' && warn( 'data functions should return an object:\n' + 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function', vm ) } // proxy data on instance const keys = Object.keys(data) const props = vm.$options.props const methods = vm.$options.methods let i = keys.length while (i--) { const key = keys[i] if (process.env.NODE_ENV ! == 'production') { if (methods && hasOwn(methods, key)) { warn( `Method "${key}" has already been defined as a data property.`, vm ) } } if (props && hasOwn(props, key)) { process.env.NODE_ENV ! == 'production' && warn( `The data property "${key}" is already declared as a prop. ` + `Use prop default value instead.`, vm ) } else if (! IsReserved (key)) {proxy(VM, '_data', key)}} // Observe data // Set the data on the data object to reactive observe(data, true /* asRootData */) }Copy the code

initComputed

src/core/instance/state.js

* Three things: * 1. Create a watcher instance for computed[key]. The default is lazy execution * 2. * @param {*} Computed = {* key1: function() {return xx}, * key2: {* get: function() { return xx }, * set: function(val) {} * } * } */ function initComputed (vm: Component, computed: Object) { // $flow-disable-line const watchers = vm._computedWatchers = Object.create(null) // computed properties are just getters during SSR const isSSR = isServerRendering() for (const key in computed) { const userDef = computed[key] const getter = typeof userDef === 'function' ? userDef : userDef.get if (process.env.NODE_ENV ! == 'production' && getter == null) { warn( `Getter is missing for computed property "${key}".`, vm ) } if (! isSSR) { // create internal watcher for the computed property. watchers[key] = new Watcher( vm, getter || noop, noop, computedWatcherOptions ) } // component-defined computed properties are already defined on the // component prototype. We only need to define computed properties defined // at instantiation here. if (! (key in vm)) { defineComputed(vm, key, userDef) } else if (process.env.NODE_ENV ! == 'production') { if (key in vm.$data) { warn(`The computed property "${key}" is already defined in data.`, vm) } else if (vm.$options.props && key in vm.$options.props) { warn(`The computed property "${key}" is already defined as a prop.`, vm) } else if (vm.$options.methods && key in vm.$options.methods) { warn(`The computed property "${key}" is already Defined as a method. ', VM)}}}} /** * Agent key in computed objects to target (VM) */ export function defineComputed (target: any, key: string, userDef: Object | Function ) { const shouldCache = ! isServerRendering() if (typeof userDef === 'function') { sharedPropertyDefinition.get = shouldCache ? createComputedGetter(key) : createGetterInvoker(userDef) sharedPropertyDefinition.set = noop } else { sharedPropertyDefinition.get = userDef.get ? shouldCache && userDef.cache ! == false ? createComputedGetter(key) : createGetterInvoker(userDef.get) : noop sharedPropertyDefinition.set = userDef.set || noop } if (process.env.NODE_ENV ! == 'production' && sharedPropertyDefinition.set === noop) { sharedPropertyDefinition.set = function () { warn( `Computed  property "${key}" was assigned to but it has no setter.`, this ) } } Object.defineProperty(target, key, SharedPropertyDefinition)} /** * @returns a function that is executed when the vm.computedProperty is accessed, */ function createComputedGetter (key) {return function computedGetter () {const watcher = this._computedWatchers && this._computedWatchers[key] if (watcher) { if (watcher.dirty) { watcher.evaluate() } if (Dep.target) { watcher.depend() } return watcher.value } } }Copy the code

initWatch

src/core/instance/state.js

* @param {*} watch = {* 'key1'; * @param {*} watch = {* 'key1'; function(val, oldVal) {}, * 'key2': 'this.methodName', * 'key3': { * handler: function(val, oldVal) {}, * deep: true * }, * 'key4': [ * 'this.methodNanme', * function handler1() {}, * { * handler: function() {}, * immediate: true * } * ], * 'key.key5' { ... } * } */ function initWatch (vm: Component, watch: Object) { for (const key in watch) { const handler = watch[key] if (Array.isArray(handler)) { for (let i = 0; i < handler.length; I ++) {createWatcher(VM, key, handler[I])}} else {createWatcher(VM, key, handler)}}} /** * $watch * @returns */ function createWatcher (vm: Component, expOrFn:) string | Function, handler: any, options? : Object ) { if (isPlainObject(handler)) { options = handler handler = handler.handler } if (typeof handler === 'string') { handler = vm[handler] } return vm.$watch(expOrFn, handler, options) } export function stateMixin (Vue: Class<Component>) { // flow somehow has problems with directly declared definition object // when using Object.defineProperty, so we have to procedurally build up // the object here. const dataDef = {} dataDef.get = function () { return this._data  } const propsDef = {} propsDef.get = function () { return this._props } if (process.env.NODE_ENV ! == 'production') { dataDef.set = function () { warn( 'Avoid replacing instance root $data. ' + 'Use nested data properties instead.', this ) } propsDef.set = function () { warn(`$props is readonly.`, this) } } Object.defineProperty(Vue.prototype, '$data', dataDef) Object.defineProperty(Vue.prototype, '$props', PropsDef) vue.prototype.$set = set vue.prototype.$delete = del /** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Unwatch * @param {*} expOrFn key * @param {*} cb callback * @param {*} options $watch = function (expOrFn: export/export/export/export/export/export/export/export/export/export/export/export/export/export/export/export/export/export/export/export/export/export/export/export/export/export/export string | Function, cb: any, options?: Object ): Function { const vm: Component = this if (isPlainObject(cb)) { return createWatcher(vm, expOrFn, cb, options) } options = options || {} options.user = true const watcher = new Watcher(vm, expOrFn, cb, options) if (options.immediate) { const info = `callback for immediate watcher "${watcher.expression}"` pushTarget() invokeWithErrorHandling(cb, vm, [watcher.value], vm, info) popTarget() } return function unwatchFn () { watcher.teardown() } } }Copy the code

observer

src/core/observer/index.js

We called observe(data, initData, Attempt to create an observer instance for a value, /** * Attempt to create an observer instance, * returns the new observer if successfully observed, * Or the existing observer if the value already has one. * * The actual entry for reactive processing * creates an observer instance for the object, and returns the existing observer instance if the object has already been observed, Otherwise create a new observer instance * @param {*} value object => {} */ export function observe (value: any, asRootData:? boolean): Observer | void { if (! isObject(value) || value instanceof VNode) { return } let ob: Observer | void if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { ob = value.__ob__ } else if ( shouldObserve && ! isServerRendering() && (Array.isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && ! _isVue) {// create Observer instance ob = new Observer(value)} if (asRootData && ob) {ob.vmcount ++} return ob}Copy the code

Observer

src/core/Observer/index.js

/** * Observer class that is attached to each observed * object. Once attached, the observer converts the target * object's property keys into getter/setters that * collect dependencies and dispatch * * The observer class is attached to each object being observed, value.__ob__ = this * and the properties of the object are converted to getters/setters. */ export class Observer {value: any; dep: Dep; vmCount: number; // number of vms that have this object as root $data constructor (value: Dep = new Dep() this.vmcount = 0 def(value, '__ob__'), This) /** * value is an array * hasProto = '__proto__' in {} * is used to determine whether an object has a __proto__ attribute, __proto__ is not a standard attribute, so some browsers do not support __proto__. For example, ie6-10, Opera10.1 * Because we're going to overwrite the array's default seven stereotype methods with __proto__ manipulating the data's stereotype chain *, */ If (array.isarray (value)) {if (hasProto) {protoAugment(value, arrayMethods)} else {copyAugment(value, } this.observeArray(value)} else {// value is an object, This.walk (value)}} /** * walk through all properties and convert them into * getter/setters. This method should only be called when * value type is Object. * * Iterate over each key on the Object, */ walk (obj: Object) {const keys = object.keys (obj) for (let I = 0; i < keys.length; I ++) {defineReactive(obj, keys[I])}} /** * Observe a list of Array items. ** Observe a list of Array items. * */ observeArray (items: Array<any>) {for (let I = 0, l = items.length; i < l; i++) { observe(items[i]) } } }Copy the code

defineReactive

src/core/observer/index.js

* Define a reactive property on an Object. * * Block obj[key] */ define function defineReactive (obj:) */ define function defineReactive (obj:) */ define function defineReactive (obj: Object, key: string, val: any, customSetter? :? Function, shallow? : Boolean) {// instantiate dep, A key a dep const dep = new dep () const property = Object. GetOwnPropertyDescriptor (obj, key) if (property && property.configurable === false) { return } // cater for pre-defined getter/setters const getter = property && property.get const setter = property && property.set if ((! getter || setter) && arguments.length === 2) { val = obj[key] } let childOb = ! shallow && observe(val) Object.defineProperty(obj, key, { enumerable: true, configurable: True, /** * dep. target is a static property of the Dep class with a value of watcher and is set when watcher is instantiated * the callback function passed on new Watcher (except computed, (Dep. Target is set to NULL after the callback function is executed to prevent repeated dependency collection */ get: function reactiveGetter () { const value = getter ? getter.call(obj) : Val if (dep.target) {// add watcher to Dep, Dep dep.depend() if (childOb) {// childOb indicates the observer object of the nested object in the object, Childob.dep. depend() if (array.isarray (value)) {dependArray(value)}}} return value}, set: function reactiveSetter (newVal) { const value = getter ? getter.call(obj) : val /* eslint-disable no-self-compare */ if (newVal === value || (newVal ! == newVal && value ! == value)) { return } /* eslint-enable no-self-compare */ if (process.env.NODE_ENV ! == 'production' && customSetter) { customSetter() } // #7981: for accessor properties without setter if (getter && ! Return if (set.call (obj, newVal)) {else {val = newVal}} else {val = newVal} Shallow && Observe (newVal) // Dependent notification update dep.notify()}})}Copy the code

dependArray

src/core/observer/index.js

* Collect dependencies on array elements when the array is touched, Since * we cannot intercept array element access like property getters. Function dependArray (value: Array<any>) {for (let e, I = 0, l = value.length; i < l; i++) { e = value[i] e && e.__ob__ && e.__ob__.dep.depend() if (Array.isArray(e)) { dependArray(e) } } }Copy the code

Array response

src/core/observer/array.js

* not type checking this file because flow doesn't play well with * dynamically accessing methods on Array prototype * * ArrayMethods (array. prototype) : arrayMethods (array. prototype) : arrayMethods (array. prototype) : arrayMethods (array. prototype) : arrayMethods (array. prototype) : arrayMethods (array. prototype) : arrayMethods (array. prototype) : arrayMethods (array. prototype) : arrayMethods (array. prototype) /util/index' const arrayProto = Array.prototype export const arrayMethods = Object.create(arrayProto) const methodsToPatch = [ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ] /** * Intercept mutating methods and emit events */ methodsToPatch.forEach(function (method) { // cache original method const original = arrayProto[method] def(arrayMethods, method, function mutator (... args) { const result = original.apply(this, args) const ob = this.__ob__ let inserted switch (method) { case 'push': case 'unshift': inserted = args break case 'splice': inserted = args.slice(2) break } if (inserted) ob.observeArray(inserted) // notify change ob.dep.notify() return result })})Copy the code

Dep

src/core/observer

import type Watcher from './watcher' import { remove } from '.. /util/index' import config from '.. /config' let uid = 0 /** * A DEp is an Observable that can have multiple * Directives subscribing to it. ** One DEP corresponds to one Key * Is responsible for collecting dependencies when reading reactive data. What watcher is responsible for each DEP (or obj.key) dependency * When updating reactive data, */ export default class dep {static target:? Watcher; id: number; subs: Array<Watcher>; constructor () { this.id = uid++ this.subs = [] } addSub (sub: Watcher) { this.subs.push(sub) } removeSub (sub: Watcher) { remove(this.subs, sub) } depend () { if (Dep.target) { Dep.target.addDep(this) } } notify () { // stabilize the subscriber list first const subs = this.subs.slice() if (process.env.NODE_ENV ! == 'production' && ! config.async) { // subs aren't sorted in scheduler if not running async // we need to sort them now to make sure they fire in correct // order subs.sort((a, b) => a.id - b.id) } for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } } } // The current target watcher being evaluated. // This is globally unique because only one // 设 计 工 作 工 作 的 工 作, Target = The watcher currently executing * is assigned by calling pushTarget. */ dep. target = null const targetStack = [] export function pushTarget (target:? Watcher) { targetStack.push(target) Dep.target = target } export function popTarget () { targetStack.pop() Dep.target = targetStack[targetStack.length - 1] }Copy the code

watcher

src/core/observer/watcher.js


import {
  warn,
  remove,
  isObject,
  parsePath,
  _Set as Set,
  handleError,
  invokeWithErrorHandling,
  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.
 * * 一个组件一个 watcher(渲染 watcher)或者一个表达式一个 watcher(用户watcher)
 * 当数据更新时 watcher 会被触发,访问 this.computedProperty 时也会触发 watcher
 */
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.
   * 执行 this.getter,并重新收集依赖
   * this.getter 是实例化 watcher 时传递的第二个参数,一个函数或者字符串,比如:updateComponent 或者 parsePath 返回的读取 this.xx 属性值的函数
   * 为什么要重新收集依赖?
   *   因为触发更新说明有响应式数据被更新了,但是被更新的数据虽然已经经过 observe 观察了,但是却没有进行依赖收集,
   *   所以,在更新页面时,会重新执行一次 render 函数,执行期间会触发读取操作,这时候进行依赖收集
   */
  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.
   * * 两件事:
   *   1、添加 dep 给自己(watcher)
   *   2、添加自己(watcher)到 dep
   */
  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.
   * * 根据 watcher 配置项,决定接下来怎么走,一般是 queueWatcher
   */
  update () {
    /* istanbul ignore else */
    if (this.lazy) {
      // 懒执行时走这里,比如 computed
      // 将 dirty 置为 true,可以让 computedGetter 执行时重新计算 computed 回调函数的执行结果
      this.dirty = true
    } else if (this.sync) {
      // 同步执行,在使用 vm.$watch 或者 watch 选项时可以传一个 sync 选项,
      // 当为 true 时在数据更新时该 watcher 就不走异步更新队列,直接执行 this.run 
      // 方法进行更新
      // 这个属性在官方文档中没有出现
      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) {
          const info = `callback for watcher "${this.expression}"`
          invokeWithErrorHandling(this.cb, this.vm, [value, oldValue], this.vm, info)
        } else {
          this.cb.call(this.vm, value, oldValue)
        }
      }
    }
  }

  /**
   * Evaluate the value of the watcher.
   * This only gets called for lazy watchers.
   *  * 懒执行的 watcher 会调用该方法
   *   比如:computed,在获取 vm.computedProperty 的值时会调用该方法
   * 然后执行 this.get,即 watcher 的回调函数,得到返回值
   * this.dirty 被置为 false,作用是页面在本次渲染中只会一次 computed.key 的回调函数,
   *   这也是大家常说的 computed 和 methods 区别之一是 computed 有缓存的原理所在
   * 而页面更新后会 this.dirty 会被重新置为 true,这一步是在 this.update 方法中完成的
   */
  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
    }
  }
}
Copy the code

conclusion

The Dep and Wathcer are difficult to understand. For each component in vue2.x, there is a render Watcher, and then for initComputed and initWatch, there is a user Watcher, and for Dep, there is a Dep for each property in Data. Dep: Dep Watcher: Dep Watcher: Dep Watcher: Dep Watcher: Dep Watcher: Dep Watcher: Dep Watcher: Dep Watcher: Dep Watcher

Another common problem is the difference between watch and computed. There is essentially no difference between watch and computed. For example, computed is used in a many-to-one relationship. The value will be recalculated. Watch is suitable for one-to-many relationships, listening for a change in a value to trigger the corresponding callback, and can also support asynchronous.

About Vue response principle is about these, usually see more understanding, more debugger print, for example, we print a Vue instance, you can see for example, the value in data has a corresponding Dep, open Dep has Wathcher, similar you can observe more.