current position:Home>[Vue source code 02] computed response - initialization, access, update process

[Vue source code 02] computed response - initialization, access, update process

2021-08-25 09:33:30 woow_ wu7

Navigation

[[ thorough 01] Execution context ](https://juejin.im/post/684490...)
[[ thorough 02] Prototype chain ](https://juejin.im/post/684490...)
[[ thorough 03] Inherit ](https://juejin.im/post/684490...)
[[ thorough 04] The event loop ](https://juejin.im/post/684490...)
[[ thorough 05] currying Partial function Function memory ](https://juejin.im/post/684490...)
[[ thorough 06] Implicit conversion and Operator ](https://juejin.im/post/684490...)
[[ thorough 07] Browser caching mechanism (http Caching mechanisms )](https://juejin.im/post/684490...)
[[ thorough 08] Front end security ](https://juejin.im/post/684490...)
[[ thorough 09] Depth copy ](https://juejin.im/post/684490...)
[[ thorough 10] Debounce Throttle](https://juejin.im/post/684490...)
[[ thorough 11] Front-end routing ](https://juejin.im/post/684490...)
[[ thorough 12] Front-end modularization ](https://juejin.im/post/684490...)
[[ thorough 13] Observer mode Publish subscribe mode Two way data binding ](https://juejin.im/post/684490...)
[[ thorough 14] canvas](https://juejin.im/post/684490...)
[[ thorough 15] webSocket](https://juejin.im/post/684490...)
[[ thorough 16] webpack](https://juejin.im/post/684490...)
[[ thorough 17] http and https](https://juejin.im/post/684490...)
[[ thorough 18] CSS-interview](https://juejin.im/post/684490...)
[[ thorough 19] Handwriting Promise](https://juejin.im/post/684490...)
[[ thorough 20] Handwritten functions ](https://juejin.im/post/684490...)

[[react] Hooks](https://juejin.im/post/684490...)

[[ Deploy 01] Nginx](https://juejin.im/post/684490...)
[[ Deploy 02] Docker Deploy vue project ](https://juejin.im/post/684490...)
[[ Deploy 03] gitlab-CI](https://juejin.im/post/684490...)

[[ Source code -webpack01- Pre knowledge ] AST Abstract syntax tree ](https://juejin.im/post/684490...)
[[ Source code -webpack02- Pre knowledge ] Tapable](https://juejin.im/post/684490...)
[[ Source code -webpack03] Handwriting webpack - compiler Simple compilation process ](https://juejin.im/post/684490...)
[[ Source code ] Redux React-Redux01](https://juejin.im/post/684490...)
[[ Source code ] axios ](https://juejin.im/post/684490...)
[[ Source code ] vuex ](https://juejin.im/post/684490...)
[[ Source code -vue01] data Response type and Initialize rendering ](https://juejin.im/post/684490...)
[[ Source code -vue02] computed Response type - initialization , visit , The update process ](https://juejin.im/post/684490...)
[[ Source code -vue03] watch Listening properties - Initialization and update ](https://juejin.im/post/684490...)
[[ Source code -vue04] Vue.set and vm.$set ](https://juejin.im/post/684490...)
[[ Source code -vue05] Vue.extend ](https://juejin.im/post/684490...)

[[ Source code -vue06] Vue.nextTick and vm.$nextTick ](https://juejin.im/post/684790...)

Pre knowledge

Learning goals

  • computed Calculation properties are only available in computed When interviewed , To calculate

    • Because in new Watcher yes computed watcher when , namely lazy=true when , There is no immediate execution in the constructor get() Method , Instead, it is triggered when the calculated property is accessed computed In response get after , Executive get Method computed getter function
  • computed The calculated attribute has caching function

    • adopt dirty=true when , To execute watcher.evaluate() Method , Will execute computed wawtcher Medium get() namely computd Defined function , Calculate again , After calculation , take this.dirty=false
    • On the next visit , Will judge first dirty, yes false Just return the cached value directly
  • computed The dependency must be responsive data , Otherwise, even dependency changes will not trigger computed Recalculate
  • Even if computed Our dependence has changed , however computed If the calculated value does not change , No new rendering
  • computed watcher and render watcher and dep and dep.subs and watcher.deps A complex relationship between

    • computed Access to the process

      • visit computed, Trigger computed Responsive get function ,get Function to determine if dirty=true, Then perform computed watchet Of evalute Method , namely watcher Medium get() Method , And then put Dep.target = computed watcher, perform computed getter The function is user specified computed function
      • perform computed getter The time of the function , Because there are dependent, responsive data, So it triggers data Of get function , perform dep.depend(), Is to put computed watcher Medium newDeps Add computed Dependent dep, Simultaneous direction computed Dependent dep Of subs Add computed watcher, Then I put Dep.target = targetStack The previous in the array watcher, Then return the result of the calculation , And the dirty=false
      • And then determine Dep.taret There is , Is executed computed watcher Of depend Method , Loop traversal computed watcher Medium deps, Take out dep, perform dep.depend
      • Because the Dep.target = render watcher, therefore dep.depend Will send to render watcher Of newDeps Add data Of dep, towards data Of dep Medium subs Add render watcher, So the computed Calculate the dependency of the property dep Medium subs Namely [computed watcher, render watcher] This ensures that the rendering time ,computed Precede render Execute first , Guarantee computed Valuable
    • comuted The update process

      • In the example above ,change The process of , It's a change computed Dependence , Then a series of processes
      • Trigger computed Responsive dependent dep.notify() Method , Loop through the dependent dep.subs Every one of the arrays watcher.update() Method , In the example above subs=[computed watcher, render watcher] So first execute computed watcher
      • compued watcher

        • stay update In the method , Will judge lazy=true?, because computed watcher Of lazy=true, So execute dirty=true
      • render watcher

        • stay update In the method , lazy Not for true, Will execute queueWatcher(this), Is to call rendering watcher Re evaluate and render

Some words

internal: Inside 

Scheduler: Scheduler 
queue: queue 
( flushSchedulerQueue :  Refresh scheduler queue  )

computed Source code

(1) computed The initialization

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

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

  • I have mainly done the following things

    • <font color=blue size=5>(1-1-1) new Watcher(vm, getter, noop, computedWatcherOptions) </font>

      • new One. computed watcher
      • Parameters

        • computedWatcherOptions

          • How to know is a computed watcher => Mainly through article 4 Parameters computedWatcherOptions => { lazy: true }, namely comoputed watcher Of lazy The attribute is true
        • getter

          • It is defined by the user computed Function in object
        • noop

          • It's a null function
      • Be careful :

        • stay new Watcher Compute properties watcher In the constructor of

          • this.value=this.lazy ? undefined : this.get()
          • Because computing properties watcher Of lazy=true, So it won't be executed immediately get() Method
          • <font color=blue> When will it be implemented get() Well ?</font>

            • <font color=blue> The timing of execution is template I visited computed</font>, because computed The response expression is also defined , Visited computed Property will execute computed Of get Method , stay get Method watcher.evaluate() Method , In it is to perform get(), To calculate computed Result
    • <font color=blue size=5>(1-1-2) hold computd Defined as responsive </font>

      • defineComputed(vm, key, userDef) => Object.defineProperty(target, key, sharedPropertyDefinition) => sharedPropertyDefinition.get => createComputedGetter(key) => computedGetter
      • That is to say, visit computed Medium this.xxxx They will execute computedGetter function
    • <font color=blue size=5>(1-1-3) computedGetter - This method is very important !!!!!!!!!!!!!!!!!!!!!</font>

      • watcher.evaluate() Perform calculation of properties watcher Medium evaluate Method

        • When dirty=true, also watcher In existence , Will execute computed watcher Of evalute Method
        • <font color=blue>evalute</font> Method will execute <font color=blue>get()</font> Method , And will <font color=blue>this.dirty</font> Change it to <font color=blue>false</font>

          • <font color=blue>get()</font>

            • <font color=blue>pushTarget(this)</font>

              • <font color=blue> towards targetStack Array push One computed watcher</font>
              • <font color=blue> take Dep.target Designated as computed watcher</font>
            • Execute user in computed Methods defined in objects , namely getter Method newName

              • such as computed: {newName() {return this.name + 'new' }}
              • Be careful :

                • In the process, it will trigger data The influence formula of the object , namely this.name Trigger response data Medium get function , Because I visited data Of name attribute
                • data,computed Each has its own response
                • here data The response will collect the information of the calculated attributes watcher, This will be sorted out in the later access process of calculation properties

                  • Mainly

                    • towards computed watcher Of newDeps Add render watcher Of dep
                    • towards render watcher Of dependent properties dep Of subs Add computed watcher
                    • See below
      • watcher.depend() Perform calculation of properties watcher Of depend Method

        • Put it below and analyze it together with the access process
  • Source code
  • Vue.prototype._init => initState(vm) => initComputed(vm, opts.computed)
  • initComputed - scr/core/instance/state.js

    initComputed - scr/core/instance/state.js
    ---
    
    function initComputed (vm: Component, computed: Object) {
    const watchers = vm._computedWatchers = Object.create(null)
    //  Statement  watchers  and  _computedWatchers  For an empty object 
    
    const isSSR = isServerRendering()
    //  Whether it is ssr Environmental Science 
    
    for (const key in computed) {
      const userDef = computed[key]
      // userDef  yes  computed  Of  getter  function 
    
      const getter = typeof userDef === 'function' ? userDef : userDef.get
      // getter
        // computed getter  It could be a function   perhaps   have get Object of method 
        //  This is generally a function , And the function needs return A value 
    
      if (process.env.NODE_ENV !== 'production' && getter == null) {
        warn(
          `Getter is missing for computed property "${key}".`,
          vm
        )
      }
      //  If it is not a function or object, it will report a warning 
    
    
      if (!isSSR) {
        // create internal watcher for the computed property.
        //  Not ssr Environmental Science , Browser environment , Just create a new one  computed watcher
        // computed watcher
          // computedWatcherOptions = { lazy: true }
          // getter =  User defined computed Function in object 
    
        watchers[key] = new Watcher(
          vm,
          getter || noop, //  User defined computed Function in object , namely  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.
      //  stay  vue.extends  and  new Vue()  In the process, the responsive computed
    
      if (!(key in vm)) {
        defineComputed(vm, key, userDef)
        // defineComputed  take  computed  Become responsive 
    
      } else if (process.env.NODE_ENV !== 'production') {
        //  Deal with duplicate names , stay props,data,computed You can't use the same name key
        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)
        }
      }
    }
    }
  • defineComputed - scr/core/instance/state.js

    defineComputed - scr/core/instance/state.js
    ---
    
    export function defineComputed (
    target: any,
    key: string,
    userDef: Object | Function
    ) {
    const shouldCache = !isServerRendering()
    // shouldCache  If in the browser environment, it is  true
    
    if (typeof userDef === 'function') {
      sharedPropertyDefinition.get = shouldCache
        ? createComputedGetter(key) //  Definition computed When interviewed , The trigger get
        : createGetterInvoker(userDef)
      sharedPropertyDefinition.set = noop
    } else {
      // userDef  No  function, We just ignore 
      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)
    //  Define responsive  computed
      // 1.  When passed  this.xxxx  visit computed, It will trigger sharedPropertyDefinition Object get
      // 2. get  It's actually below createComputedGetter Back to  computedGetter function 
    }
  • 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]
      //  Take out each  computed watcher
    
      if (watcher) {
        if (watcher.dirty) {
          // watcher.dirty
            // 1.  Default initialization ,comoputed watcher  Of  dirty=true
            // 2.  When  dirty=true  Will execute  watcher.evaluate()
            // 3. watcher.evaluate()  After the execution , dirty=false
            //  summary :  dirty=true => watcher.evaluate() => dirty=false
    
          watcher.evaluate()
          // watcher.evaluate()
            // 1.  Will execute  computed watcher  Medium  get()
              // pushTarget(this)
                // 1.  take  computed watcher  Add to   targetStack  Array 
                // 2.  take  Dep.target = computed watcher
              //  perform  this.getter.call(vm, vm)  That is, user-defined  computed Methods in objects 
                // 1.  Columns such as : computed: {newName() {return this.name + 'new' }}
                // 2.  because :computed Of newName In the method , Rely on data Medium this.name, Access to this.name It will trigger data Responsive get Method 
                // 3.  therefore :ata Responsive get Method execution process is as follows 
                  //  Got it this.name Value 
                  //  here ,Dep.target  yes computed watcher
                  //  And then execute this.name Object's dep Class depend Method for dependency collection 
                    //  towards  computed watcher  Of newDeps Add render watcher Of dep
                    //  towards  render watcher  Of  subs  Add  computed watcher
              //  popTarget()
                // 1. targetStack.pop() take  computed watcher from targetStack Delete in array 
                // 2.  And will  Dep.target  Specify as the previous... In the array  watcher, No, just undefined
            // 2.  take  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
      }
    }
    }
  • Watcher - scr/core/observer/watcher.js

    Watcher - scr/core/observer/watcher.js
    ---
    
    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
      }
    }
    }

(2) computed Access process

  • Case study

    <!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>
  • stay vm._update(vm._render(), hydrating) In the process , When in template The template uses computed Object key When , It will trigger computed Of responsive objects get Method
  • computed In response get The way is computedGetter Method , In this method , Will judge wathcer and watcher.dirty Whether there is , The proof of existence is computed watcher, Will execute computed wathcer Of watcher.evaluate() Method
  • watcher.evaluate() Method will be executed computed watcher Medium get() Method , And will this.dirty Change it to false
  • watcher.evaluate()

    • get() Method

      • call <font color=red>pushTarget(this)</font>

        • <font color=red> towards targetStack Array push One computed watcher</font>
        • <font color=red> take Dep.target Designated as computed watcher</font>
      • And then call computed getter Method , That is, user-defined computed Methods in objects

        • such as : computed: {newName() {return this.name + 'new' }}
        • because :computed Of newName In the method , Rely on data Medium this.name, Access to this.name It will trigger data Responsive get Method
        • therefore :<font color=DarkOrChid>data Responsive get Method execution process is as follows :</font>

          • Got it this.name Value
          • here ,Dep.target yes computed watcher

            • And then execute this.name Corresponding dep Class depend Method for dependency collection

              • <font color=DarkOrChid> towards computed watcher Of newDeps Add render watcher It's the same as data Attribute dep</font>
              • <font color=DarkOrChid> towards render watcher Corresponding data Attribute dep Example of subs Add computed watcher</font>
              • <font color=DarkOrChid> Equal to say data Of this.name and computed Watcher Have the same dep example </font>
            • After performing the above steps dep.subs and computed watcher.newDeps The state of is

              • this.name Corresponding dep In the instance subs = [computedWatcher]
              • computed watcher Medium newDeps = [ above this.name Corresponding dep]
          • return name Value
      • call <font color=red>popTarget()</font>

        • <font color=red>targetStack.pop() take computed watcher from targetStack Delete in array </font>
        • <font color=red> And will Dep.target Specify as the previous... In the array watcher</font>

          • <font color=red>Dep.target = render watcher</font>
          • In the example above targetStack Array is executing computed Of getter Method has two members
          • first :render watcher
          • the second :computed watcher
          • pop Then there's one left render watcher
      • Finally back to computed The calculated result value
  • watcher.depend()

    • When Dep.target In existence , Will execute watcher.depend()
    • It's done ,<font color=DarkOrChid>Dep.target = render watcher</font>
    • watcher.depend()

      • And then execute <font color=DarkOrChid>compute watcher Medium watcher.depend() Method </font>
      • then ,<font color=DarkOrChid> From back to front , Take out one by one computed watcher in deps Array dep, perform dep.depend()</font>

        • Be careful : above computed watcher Medium deps Medium dep, Namely this.name Object's dep, Inside subs There is only one in the array computedWatcher
        • contrast : stay data When the properties of an object are accessed , Will perform data Of the corresponding property dep.depend()
      • <font color=DarkOrChid>Dep.target.addDep(this)</font>

        • <font color=DarkOrChid> At this time Dep.targt yes render watcher</font>, because popTarget() operation pop Out computed watcher Then there's only one left render watcher 了
        • addDep front

          • render watcher Medium deps It's an empty array
          • render watcher Medium newDeps It's an empty array
        • addDep There are two main things to do

          • <font color=DarkOrChid> towards render watcher Of newDeps Add The render watcher Corresponding datad Attribute dep</font>
          • <font color=DarkOrChid> towards render watcher Corresponding data Property corresponds to dep Class subs Add render watcher</font>
          • After adding ,data Of dep.subs = [computed watcher, render watcher]

            • <font color=red> So when this.name The corresponding... Is triggered after the property is modified set function , It will trigger dep.notify, Then the cycle sub Medium watcher, perform watcher.update() Method </font>
            • <font color=red>[computed watcher, render watcher] This order ensures that in render When ,computed It must be worth it </font>

(3) computed The process of updating

  • In the example above ,change The process of , It's a change computed Dependence , Then a series of processes
  • Trigger computed Responsive dependent dep.notify() Method , Loop through the dependent dep.subs Every one of the arrays watcher.update() Method , In the example above subs=[computed watcher, render watcher] So first execute computed watcher
  • compued watcher

    • stay update In the method , Will judge lazy=true?, because computed watcher Of lazy=true, So execute dirty=true
  • render watcher

    • stay update In the method , lazy Not for true, Will execute queueWatcher(this), Is to call rendering watcher Re evaluate and render

Information

Actual case - Avoid pit https://juejin.im/post/684490...
detailed https://juejin.im/post/684490...
Source version https://juejin.im/post/684490...
computed watcher https://juejin.im/post/684490...

copyright notice
author[woow_ wu7],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2021/08/20210825093326684y.html

Random recommended