navigation

[Deep 01] Execution context [Deep 02] Prototype chain [Deep 03] Inheritance [Deep 04] Event loop [Deep 05] Curri Bias function [Deep 06] Function memory [Deep 07] Implicit conversions and operators [Deep 07] Browser caching mechanism (HTTP caching mechanism) [Deep 08] Front-end security [Deep 09] Deep copy [Deep 10] Debounce Throttle [Deep 10] Front-end routing [Deep 12] Front-end modularization [Deep 13] Observer mode Publish subscribe mode Bidirectional data binding [Deep 14] Canvas [Deep 15] webSocket Webpack HTTP and HTTPS CSS- Interview Handwriting Promise

[react] Hooks

[Deployment 01] Nginx [Deployment 02] Docker deployVue project [Deployment 03] gitlab-CI

[source code – Webpack01 – precompiler] AST abstract syntax tree [source code – Webpack02 – Precompiler] Tapable [source code – Webpack03] hand written webpack-compiler simple compilation process [source code] Redux React-redux01 [source] Axios [source] vuex [source -vue01] Data reactive and initialize render [source -vue02] Computed responsive – Initialize, access, Update Procedure [source -vue04] Watch Listening properties – Initialize and update [source -vue04] vue. set and vm.$set [source -vue05] vue.extend

Vue. NextTick and VM.$nextTick

Front knowledge

Learning goals

  • Computed computed attributes are computed only when computed is accessed
    • Because when new Watcher is computed Watcher, that is, lazy=true, the get() method is not executed immediately in the constructor, but after the reactive GET of computed is triggered when the computed property is accessed, The get method that executes goes back to calling the computed Getter function
  • The computed computing attribute has the cache function
    • If dirty=true, the watcher.evaluate() method is executed, and the get() function defined by computd in computed Wawtcher is executed. This. Dirty =false after the calculation
    • The next time the data is accessed, dirty is determined first, and if false, the cache value is returned
  • Computed dependencies must be responsive data; otherwise, even if the dependency changes do not trigger computed recalculation
  • The new render will not occur even if the computed dependency has changed but the computed value has not
  • Computed Watcher and Render Watcher are in complex relationships with DEp and dep.subs and watcher.deps
    • Computed Access Process
      • Access computed and trigger the COMPUTED responder GET function. If dirty=true, execute the evalute method of computed Watchet, that is, the get() method of watcher. Then set dep. target to computed Watcher and execute computed Getter to the user-defined computed function
      • When executing a computed Getter function, it triggers the get function of the data because it depends on the responsive data, and executes dep.depend(), which is to add the DEp of the computed dependency to newDeps in computed Watcher. Add a computed Watcher to the subs of the DEP of the computed dependency, then add the previous watcher in the dep. target = targetStack array, and return the computed result with dirty=false
      • Then determine that dep. taret exists, run the depend method of computed Watcher, loop through deps in computed Watcher, take out Dep, and run dep.depend
      • Dep. Target = render watcher, so Dep. Depend will add data Dep to render watcher newDeps, add subs to render watcher Dep. In this case, the subs in the DEP of the dependency of the computed attribute is computed watcher, render watcher. This ensures that computed is executed before render and that computed has a value
    • Comuted update process
      • In the example above, the process of change is a series of processes after changing the computed dependency
      • The dep.notify() method that triggers a computed responder dependency loops through each watcher.update() method in the dep.subs array for that dependency, In the preceding example, subs is [computed Watcher, render watcher], so computed Watcher is executed first
      • compued watcher
        • In the update method, lazy=true? For computed Watcher, the value of dirty is true
      • render watcher
        • In the update method, lazy is not true and queueWatcher(this) is executed, which calls the render watcher to re-evaluate and re-render

Some words

FlushSchedulerQueue: flushSchedulerQueue: flushSchedulerQueue: flushSchedulerQueueCopy the code

The computed source

(1) Computed initialization

  • Vue.prototype._init= >initState(vm)= >initComputed(vm, opts.computed)

(1-1) initComputed(vm, opts.computed)

  • We have mainly done the following

    • (1-1-1) new Watcher(vm, getter, noop, computedWatcherOptions)

      • New A computed Watcher is configured
      • parameter
        • computedWatcherOptions
          • How do I know that a compute Watcher is configured? => computedWatcherOptions => {lazy: true}, that is, the lazy attribute of comoputed Watcher is true
        • getter
          • Is a user-defined function on a computed object
        • noop
          • It’s an empty function
      • Note:
        • In the constructor of the new Watcher computed property Watcher
          • this.value=this.lazy ? undefined : this.get()
          • Because watcher’s lazy=true is evaluated, the get() method is not executed immediately
          • So when do I do get()?
            • The execution time is when you access computed in the template, because computed defines the response, and when you access the computed property, the GET method of computed is executed, which is executed in the GET methodwatcher.evaluate()Method, inside, is to executeget()To calculate computed results
    • Computd is defined as the response

      • defineComputed(vm, key, userDef)= >Object.defineProperty(target, key, sharedPropertyDefinition)= >sharedPropertyDefinition.get= >createComputedGetter(key)= >computedGetter
      • That is, access computedthis.xxxxI’m going to do itcomputedGetterfunction
    • (1-1-3) computedGetter – this method is very important!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

      • Watcher.evaluate () performs the evaluate method in the computed property watcher
        • When dirty=true and watcher exists, the evalute method of computed Watcher is executed
        • The evalute method executes the get() method and changes this.dirty to false
          • get()
            • pushTarget(this)
              • Push a computed Watcher to the targetStack array
              • Target is specified as computed Watcher
            • Executes a method defined by the user on a computed object, that is, the getter method newName
              • Such ascomputed: {newName() {return this.name + 'new' }}
              • Note:
                • During this process, the impact of the data object, this.name, triggers the get function in the reactive data because the name property of the data is accessed
                • Data and computed have their own responses
                • In this case, the response of data will collect the watcher who calculates the property, which will be sorted out in the later access process of computing the property
                  • The main is
                    • Add the DEP of Render Watcher to the newDeps of computed Watcher
                    • Add computed Watcher to the subs of the DEP of the attributes that Render Watcher depends on
                    • See below
      • Watcher.depend () performs the depend method that evaluates the property watcher
        • Together with the following access process
  • The source code

  • Vue.prototype._init => initState(vm) => initComputed(vm, opts.computed)

  • initComputed – scr/core/instance/state.js

initComputed - scr/core/instance/state.js
---

functioninitComputed (vm: Component, computed: Object) {const Watchers = vm._computedWatchers = object. create(null) // Declare both watchers and _computedWatchers as an empty const IsSSR = isServerRendering() // whether the environment isSSRfor (const key inComputed) {const userDef = computed[key] // userDef is a getter function for computed const Getter = typeof userDef ==='function'? UserDef: userDef.get // getter // computed Getter can be a function or an object that has a get methodreturnA valueif(process.env.NODE_ENV ! = ='production' && getter == null) {
      warn(
        `Getter is missing for computed property "${key}". ', VM)} // Warning if it is not a function or objectif(! isSSR) { // create internal watcherfor// Computed Watcher is created in a non-SSR environment, that is, a browser environment. // Computed Watcher // computedWatcherOptions = {lazy:true} / / getter = user custom computed in the object function, which [key] = new Watcher (vm, getter | | it, / / the user custom computed object function, Computed Getter NOOP, computedWatcherOptions, // {lazy:true} ) } // component-defined computed properties are already defined on the // component prototype. We only need to define Computed properties defined // at instantiation here. // Reactive computed is defined in both vue.extends and new vue ()if(! (keyinDefineComputed (vm, key, userDef) // defineComputed makes computed responsive}else if(process.env.NODE_ENV ! = ='production') {// for props,data, and computed, the same key cannot be usedif (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)
      }
    }
  }
}
Copy the code
  • defineComputed – scr/core/instance/state.js
defineComputed - scr/core/instance/state.js
---

export functiondefineComputed ( target: any, key: string, userDef: Object | Function ) { const shouldCache = ! IsServerRendering () // shouldCache if used in a browser environmenttrue

  if (typeof userDef === 'function') { sharedPropertyDefinition.get = shouldCache ? CreateComputedGetter (key) / / define computed is accessed, trigger the get: createGetterInvoker (userDef) sharedPropertyDefinition. Set = it}else{// userDef is notfunction, we ignore sharedPropertyDefinition directly. The 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) // Define responsive computed // 1. // 2. Get is the computedGetter function returned by createComputedGetter below.Copy the code
  • createComputedGetter – scr/core/instance/state.js
createComputedGetter - scr/core/instance/state.js
---

function createComputedGetter (key) {
  return function computedGetter() {const watcher = this._computedwatchers && this._computedwatchers [key] // Remove every computedWatcherif (watcher) {
      if(watcher.dirty) { // watcher.dirty // 1. By default, comoputed watcher's dirty is equal totrue/ / 2. When the dirty =trueWatcher.evaluate () // 3. Watcher.evaluate () completes, dirty=false// Summary: dirty=true => watcher.evaluate() => dirty=falsewatcher.evaluate() // watcher.evaluate() // 1. For computed Watcher, get() // pushTarget(this) // 1. Add the computed Watcher to the targetStack array // 2. Target = computed watcher // Run this.getter.call(VM, VM) on a user-defined computed object // 1. For example, computed: {newName() {return this.name + 'new'}} // 2. For computed, the newName method relies on this.name in data. So: The ata reactive get method executes as follows: Target is computed watcher // then execute the depend method of the Dep class of the this.name object for dependency collection // Add the Dep of Render watcher to the newDeps of computed Watcher // Add computed watcher to the subs of Render Watcher // popTarget() // 1.targetStack.pop () takes computed Watcher removes from targetStack array // 2. Dep. Target = "watcher"; Dep. Target = "watcher"; The dirty =false

        // evaluate () {
        //   this.value = this.get()
        //   this.dirty = false/ / / /}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
        // }

      }

      if (Dep.target) {
        watcher.depend()

        // depend () {
        //   let i = this.deps.length
        //   while (i--) {
        //     this.deps[i].depend()
        //   }
        // }

        
      }
      return watcher.value
    }
  }
}
Copy the code
  • Watcher – scr/core/observer/watcher.js
Watcher - scr/core/observer/watcher.js
---

exportdefault 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 = vmif (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 // forlazy 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 upfor dependency collection.
   */
  cleanupDeps () {
    let i = this.deps.length
    while (i--) {
      const dep = this.deps[i]
      if(! this.newDepIds.has(dep.id)) { dep.removeSub(this) } }lettmp = 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}}}Copy the code

(2) Computed access process

  • case
<! DOCTYPE html> <html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>Document</title>
  <script src="./vue.js"></script>
</head>
<body>
  <div id="root">
    <div>{{newName}}</div>
    <button @click="change">change</button>
  </div>
  <script>
    new Vue({
      el: '#root',
      data: {
        name: 'ssssssssssssss'
      },
      computed: {
        newName() {
          return this.name + 'new'
        }
      },
      methods: {
        change() {
          this.name = '222222222222222'
        }
      }
    })
  </script>
</body>
</html>
Copy the code
  • invm._update(vm._render(), hydrating)When a key in a computed object is used in the Template template, the GET method for a computed responsive object is triggered
  • The reactive GET method for computed is computedGetter, which determines whether wathcer and watcher. Dirty exist, and the proof of existence is computed Watcher. The watcher.evaluate() method of computed Wathcer is executed
  • The watcher.evaluate() method executes the get() method on computed Watcher and changes this.dirty to false
  • watcher.evaluate()
    • The get () method
      • Call pushTarget (this)
        • Push a computed Watcher to the targetStack array
        • Target is specified as computed Watcher
      • Then the computed Getter method, which is a user-defined method on a computed object, is invoked
        • Such as:computed: {newName() {return this.name + 'new' }}
        • This is because the newName method of computed is dependent on this.name in data, that is, when this.name is accessed, the data-responsive GET method is triggered
        • So: The data response get method executes as follows:
          • Gets the value of this.name
          • The dep. target is computed Watcher
            • Then execute the depend method of the dep class corresponding to this.name to collect dependencies
              • Add the DEP of render Watcher’s data property to the newDeps of computed Watcher
              • Add computed Watcher to the subs of the DEP instance of the data property corresponding to Render Watcher
              • This is equivalent to saying that data’s this.name and computed Watcher have the same DEP instance
            • After the preceding steps are performed, the status of dep.subs and computed Watcher. newDeps is
              • Subs = [computedWatcher] subs = [computedWatcher]
              • For computed Watcher, newDeps = [dep corresponding to this.name above]
          • Return the value of name
      • Call popTarget ()
        • Targetstack.pop () Deletes computed Watcher from the targetStack array
        • And specify dep. target as the previous watcher in the array
          • Dep.target = render watcher
          • In the example above, there are two members in the targetStack array when executing the computed getter method
          • Number one: Render Watcher
          • The second is computed Watcher
          • One render watcher is left after pop
      • Finally, the computed value is returned
  • watcher.depend()
    • Watcher.depend () is executed only if dep.target exists
    • Target = render watcher
    • watcher.depend()
      • Then execute the watcher.depend() method in compute watcher
      • Then, fetch dep from deps array in computed Watcher backwards and run dep.depend()
        • Note: The dep in deps in the above computedWatcher is the deP of the this.name object, with only one computedWatcher in the subs array
        • Dep.depend () is also executed when a property of a data object is accessed.
      • Dep.target.addDep(this)
        • In this case, Dep. Targt is render watcher, because only render watcher is available after pop target () performs a pop operation on computed Watcher
        • Before addDep
          • Deps in Render Watcher are empty arrays
          • NewDeps in Render Watcher is an empty array
        • AddDep does two things
          • Add the DEP of the datad property to The newDeps of Render Watcher
          • Add Render Watcher to the subs of the DEP class corresponding to the data property of Render Watcher
          • Subs = [computed Watcher, Render watcher]
            • So when the this.name property changes and triggers the corresponding set function, it triggers dep.notify, and then loops through the watcher in sub, executing the watcher.update() method
            • [computed watcher, render watcher] The sequence ensures that computed has a value during render

(3) Computed update process

  • In the example above, the process of change is a series of processes after changing the computed dependency
  • The dep.notify() method that triggers a computed responder dependency loops through each watcher.update() method in the dep.subs array for that dependency, In the preceding example, subs is [computed Watcher, render watcher], so computed Watcher is executed first
  • compued watcher
    • In the update method, lazy=true? For computed Watcher, the value of dirty is true
  • render watcher
    • In the update method, lazy is not true and queueWatcher(this) is executed, which calls the render watcher to re-evaluate and re-render

data

Actual case – avoid pit juejin.cn/post/684490… Detailed juejin. Cn/post / 684490… Source code version juejin. Cn/post / 684490… The computed watcher juejin. Cn/post / 684490…