computed

First there is a _computedWatchers attribute on the Vue instance, and then a watcher object is generated with each computed attribute as the key value.

Computed use generally takes two forms: one is to return a calculation function directly, and the other is to use an object object that defines a get attribute method corresponding to the calculation function. Anyway, just get the function.

  if(! isSSR) { watchers[key] =new Watcher(
        vm,
        getter || noop,
        noop,
        computedWatcherOptions
      );
  }
Copy the code

The getter of the Vue instance is the previous calculation function, and the third parameter does not need to pass an empty function. {lazy: true} is the corresponding parameter of the Watcher constructor.

Then, in turn, defineComputed properties on instances using the defineComputed method, again using the object.defineproperty API.

When using computed properties in computed, enter the defined getter method. According to the attribute value, watcher can be found in _computedWatchers.

  function createComputedGetter (key) {
    return function computedGetter () {
      var watcher = this._computedWatchers && this._computedWatchers[key];
      if (watcher) {
        if (watcher.dirty) {
          watcher.evaluate();
        }
        if (Dep.target) {
          watcher.depend();
        }
        return watcher.value
      }
    }
  }
Copy the code

The evaluate method on Watcher is called. This method calls watcher’s get method and then changes dirty to false. When the GET method executes, the computed function in the Watcher corresponding to the computed property is called. Generally, computed attributes are computed by other data. Therefore, when the function of calculating attributes is called, the GET method of other data (such as some data defined in data) will also be triggered. Thus, the current WATcher (computed Watcher) collects deP dependencies corresponding to other data. After evaluate is executed, dep. target has changed back to the outer watcher. Call the Watcher. Depend method again to store the Dep that the computed Watcher relies on into the outer Watcher. Thus, even if data in data is only referenced in computed, the corresponding DEP exists in the outer wacther.

In the update phase, the value in data is updated, and the parent component Watcher performs the update method to push the update operation into the asynchronous queue. However, the watcher that corresponds to a computed attribute through the lazy attribute only needs to change dirty back to true again and repeat the above calculation when it is referenced.

To sum up, computed attributes create a separate Watcher object for each attribute, and then calculate attribute functions that depend on other data (usually in data). Using this function accordingly, To trigger a dependency collection of its dependency data, Watcher. Depend adds the dependency to the watcher of the current Vue instance. To rely on data changes, simply turn on the dirty state in the watcher corresponding to computed data and re-trigger the calculation function when used.

watch

Similar to computed, watch defines an object. It loops through the watch object, with key values corresponding to the values to be listened on, property values divided into array and non-array types, and createWatcher handles the behavior of watch. As you can see from the whole method, watch listens for three ways to use the function. If it is an object, it responds to the function on the handler property. If it is a character, take the corresponding methods in the instance. Finally, define it directly as a function.

  function createWatcher (vm, expOrFn, handler, options) {
    if (isPlainObject(handler)) {
      options = handler;
      handler = handler.handler;
    }
    if (typeof handler === 'string') {
      handler = vm[handler];
    }
    return vm.$watch(expOrFn, handler, options)
  }
Copy the code

Then look at the $watch method on the instance

  Vue.prototype.$watch = function (expOrFn, cb, options) {
    var vm = this;
    if (isPlainObject(cb)) {
      return createWatcher(vm, expOrFn, cb, options)
    }
    options = options || {};
    options.user = true;
    var watcher = new Watcher(vm, expOrFn, cb, options);
    if (options.immediate) {
      try {
        cb.call(vm, watcher.value);
      } catch (error) {
        handleError(error, vm, ("callback for immediate watcher \"" + (watcher.expression) + "\" ")); }}return function unwatchFn () { watcher.teardown(); }}Copy the code

This is the core implementation, and, like computed, the main implementation is the Watcher constructor, which puts the Watcher instance into vM. _watchers. The second parameter expOrFn, which is a character corresponding to the expOrFn data to be listened on, returns a function that retrieves this data using parsePath, invokes the get method on Watcher, the deP corresponding to the data triggers dependent collection, The update phase is basically the same as the behavior of data in the DEP. All the Wacther in the DEP are triggered to update, and finally run is executed. The user attribute is used to wrap a try catch layer to prevent termination of the program. If immediate is true, the listener function is directly called. Returns a function that, if executed, calls Watcher’s teardown method to remove the wacther instance from the current VM and data dependencies, but does not find specific uses of the unbinding method.