Introduction to the

Hello, everyone, I am Six six. In our development, of course, with computed attributes, we know that the results are cached, so how does that work? Of course, interviews often ask about the difference between computed and Watch, so let’s take a closer look at computed.

directory

  • You know about computed properties
  • What does computed work
  • The computed principle is implemented
  • Describe the whole computed process in detail
  • Interview questions and extensions
  • conclusion

1. Computed features you know:

  • When calculating a property, use it as a normal property, without adding ().
  • Whenever the data used inside the function changes, the value of the calculated property is immediately recalculated
  • The evaluation results of calculated attributes will be cached for easy use next time. If none of the data on which the evaluated property method depends has changed, the evaluated property is not re-evaluated
  • It can be a function or an object

What is the principle of com PuTED?

One of the most common phrases you hear in learning is that computed is a special getter method. The proxy function can be combined with Watcher to implement caching and collection dependencies.

Computed attributes are cached. How do I know if the return value of a computed attribute has changed?

When dirty is true, recalculation is required. When dirty is false, recalculation is not required.

Simulate a change in the calculated property content:

  • Both the Watcher that calculates the property and the watcher within the component are notified
  • Watcher, who calculates attributes, sets his own attribute dirty to true
  • The next time the calculated property is read, the value is recalculated because dirty is true
  • The component Watcher is notified and executes the Render function to rerender

Concrete implementation of the principle of 3.com PuTED:

3.1 initComputed Initialization

const computedWatcherOptions={lazy:true}
  function initComputed(vm, computed) {
    const watchers = vm._computedWatchers = Object.create(null)
    const isSSR = isServerRendering()
    for (const key inComputed) {// userDef Our defined computed property const userDef = computed[key] // Get getter const getter = typeof userDef ==='function'? UserDef: userdef. get // non-SSR environmentif(! IsSSR) {/ / create instances which watcher [key] = new watcher (vm, getter | | it, it, computedWatcherOptions)}if(! (keyin vm)) {
        defineComputed(vm, key, userDef)
      }
    }
  }
Copy the code

Specific explanation:

  • The initComputed function takes two parameters: a VM instance and a computed object
  • A variable watchers is declared and saved in vm._computedWatchers
  • Declare the variable isSSR to determine whether it is in an SSR environment
  • Use for.. In loops a computed object to initialize each computed property
  • The value of the computed result is stored in the getter
  • Create watcher instance
  • Determines whether the value of the calculated attribute is already defined on the VM instance, and if not, executes the defineComputed function

At this point, we understand that we will finally define and initialize each calculated property by executing the defineComputed function. Now let’s look at this function.

3.2 defineComputed Defines computed attributes

  const sharePropertyDefinition={
    enumerable:true,
    configurable:true,
    get:noop,
    set:noop
  }
  functionDefineComputed (target,key,userDef){const shouldCache=! IsServerRendering () // Set as a functionif(typeof userDef==='function'){ sharePropertyDefinition.get=shouldCache? CreateComputedGetter (key) : userDef sharePropertyDefinition. Set = it} / / set for the objectelse{ sharePropertyDefinition.get=userDef.get? shouldCache&&userDef.cache! =false? createComputedGetter(key):userDef.get:noop sharePropertyDefinition.set=userDef.set? userDef.set:noop } Object.defineProperty(target,key,sharePropertyDefinition) }Copy the code

Specific explanation:

  • The sharePropertyDefinition variable is first defined for matchingObject.definePropertyUse.
  • The defineComputed function takes target,key, and userDef
  • Use the shouldCache variable to verify if you are in an SSR environment
  • Determine whether userDef is a function because we know that the computed we passed in supports functions or objects
  • If it is a function, judge shouldCache, as true execution createComputedGetter function, and assigned to sharePropertyDefinition. Get
  • If not function, wrote the get function, judge shouldCache, as true execution createComputedGetter function, and the assignment to the sharePropertyDefinition. Get
  • The set function is assigned to sharePropertyDefinition and noop is null.
  • Finally, execute Object.defineProperty to mount the attribute to the instance

Therefore, the caching and responsiveness of computed properties depends primarily on whether the getter method is set to createComputedGetter. Because it is the createComputedGetter function that is ultimately mounted to the GET method.

3.3createComputedGetter cache vs. responsivity key

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

Specific explanation:

  • In initComputed function, we define the _computedWatchers attribute, and we get the watcher defined by _computedWatchers[key], so the variable watcher is an instance of each watcher
  • Evaluate watcher.dirty () : evaluate()
  • Check the dep. target value, and if so, execute watcher. Depend ().

The code is very simple, but it needs to work with Watcher and DEP to understand it. If you don’t understand it, please refer to my article: Vue change Detection in Depth

3.4 Inseparable from Watcher

Class Watcher{constructor(VM,expOrFn,cb,options){// independent codeif(options){ this.lazy=!! options.lazy } this.dirty = this.lazy }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()
    }
  }
}
Copy the code

Evaluate: obtain the value of the expression from Watcher

  • This.deps [I] is the state on which the property depends
  • The Depend method is used to add a component’s Watcher instance to the DEP instance, meaning that the component is notified of changes to the state on which the properties depend, and more specifically, the component Watcher watches the state on which the properties depend. (How did Watcher get the components?)

4. Describe the whole computed process in detail:

  • Read calculated properties using Watcher
  • Get reads watcher. Value when it reads the data in the computed property function and defines the response
  • The watcher calculates properties and components while observing changes in the data
  • Both the calculated properties and the component Watcher are notified when the data changes
  • Component Watcher rerenders components
  • The calculated property watcher will be recalculated if dirty is true due to data changes
  • The results of the calculation are used for this rendering and are cached

5. Interview question: What is the difference between watch and computed?

In fact, I think these two functions are completely different, I don’t know why they are always compared.

  • Watch is a behavior, what needs to be done after the state changes.
  • Computed is a state, or the result of initialization of many states.

I don’t think it’s better to compare computed with filter? It’s all used to initialize the state.

  • Computed is better for computations of large amounts of data, is used repeatedly and is not updated often. Because of caching, performance is greatly improved.
  • The filter is suitable for initializing small amounts of data and cannot be too computationally intensive as it is evaluated on every render and can be updated frequently.

6. Summary

At the beginning of learning this knowledge point is really confused, a knowledge point to read many times, until the last to understand, but there are a lot of half-understanding, so I still need to continue to work hard every day.