1. Publish subscribe vs. observer

1.1 Publish and Subscribe

  • The subscriber
  • The publisher
  • The signal center

Let us suppose that there is a “signal center” to which a task “publishes” a signal when it is completed, and to which other tasks can “subscribe” to know when they are ready to perform. This is called a publish-subscribe pattern.

1.1.1 Publish/subscribe applications

  1. Vue User-defined events
let vm = new Vue()
// { 'click': [fn1, fn2], 'change': [fn] }

// Register event (subscription message)
vm.$on('dataChange'.() = > {
  console.log('dataChange')
})

vm.$on('dataChange'.() = > {
  console.log('dataChange1')})// Trigger event (publish message)
vm.$emit('dataChange')
Copy the code
  1. eventBus
// Event center
let eventHub = new Vue()

// ComponentA. vue
/ / publisher
addTodo: function () {
  // Publish a message (event)
  eventHub.$emit('add-todo', { text: this.newTodoText })
  this.newTodoText = ' '
}

// ComponentB.vue
/ / subscriber
created: function () {
  // Subscribe to message (event)
  eventHub.$on('add-todo'.this.addTodo)
}
Copy the code

1.1.2 Publish subscribe implementation

class EventEmitter { 
  constructor () { 
    // { eventType: [ handler1, handler2 ] } 
    this.subs = Object.create(null)}// Subscribe to notifications
  $on (eventType, handler) { 
    this.subs[eventType] = this.subs[eventType] || []
    this.subs[eventType].push(handler) 
  }
  // Issue a notification
  $emit (eventType) { 
    if (this.subs[eventType]) { 
      this.subs[eventType].forEach(handler= > { 
        handler() 
      }) 
    } 
  } 
}
  
/ / test
var bus = new EventEmitter() 

// Register the event
bus.$on('click'.function () { 
  console.log('click') 
})

bus.$on('click'.function () { 
  console.log('click1')})// Triggers the event
bus.$emit('click')
Copy the code

1.2 Observer Mode

The observer pattern follows the design principle that the subject and the observer are separate, not actively triggered but passively listening, and decouple

// Subject, receives state changes, triggers each observer
class Subject {
  constructor() {
    this.state = 0
    this.observers = []
  }
  getState() {
    return this.state
  }
  setState(state) {
    this.state = state
    this.notifyAllObservers()
  }
  // Subscribe to notifications
  attach(observer) {
    this.observers.push(observer)
  }
  // Execute the notification
  notifyAllObservers() {
    this.observers.forEach(observer= > {
      observer.update()
    })
  }
}

// The observer, waiting to be triggered
class Observer {
  constructor(name, subject) {
    this.name = name
    this.subject = subject
    this.subject.attach(this)}update() {
    console.log(`The ${this.name} update, state: The ${this.subject.getState()}`)}}// Test the code
let s = new Subject()
let o1 = new Observer('o1', s)
let o2 = new Observer('o2', s)
let o3 = new Observer('o3', s)

s.setState(1)
s.setState(2)
s.setState(3)
Copy the code

1.3 Differences between publish subscribe and observer patterns

Conclusion:

  • The observer pattern is scheduled by specific targets, such as when an event is triggered, Dep will call the observer method, so there is a dependency between the observer pattern subscribers and publishers.
  • The publish/subscribe pattern is invoked by a unified scheduling center, so publishers and subscribers do not need to be aware of each other’s existence.

1.4 Observer mode in Vue

// Target (publisher)
// Dependency 
class Dep { 
  constructor () { 
    // Store all observers
    this.subs = [] 
  }
  // Add an observer
  addSub (sub) { 
    if (sub && sub.update) {
      this.subs.push(sub) 
    } 
  }
  // Notify all observers
  notify () { 
    this.subs.forEach(sub= > { 
	  sub.update() 
    })
  }
}
 
// User (user)
class Watcher { 
  update () { 
	console.log('update')}}/ / test
let dep = new Dep() 
let watcher = new Watcher() 
dep.addSub(watcher) 
dep.notify()
Copy the code
  • Observer (subscriber) – Watcher
    • Update (): Specifies what to do when an event occurs
  • Target (publisher) — Dep
    • Subs array: Stores all observers
    • AddSub (): adds an observer
    • Notify (): When an event occurs, call the update() method on all observers
  • No event center

2. Vue MVVM simple implementation

2.1 the Vue

  • function
    • Responsible for receiving initialization parameters (options)
    • Responsible for injecting properties from data into Vue instances, turning them into getters/setters
    • Is responsible for calling an observer to listen for changes to all properties in the data
    • Responsible for calling compiler parsing instructions/difference expressions
  • structure

In this case, we mount the data attribute to the Vue instance so that we can use the data, instead of this.data. Can be

class Vue {
  constructor (options) {
    // 1. Save the data of the option through the attribute
    this.$options = options || {}
    this.$data = options.data || {}
    this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el

    // 2. Convert data members into getters and setters and inject them into vue instance
    this._proxyData(this.$data)
    // 3. Call the Observer object to listen for data changes
    new Observer(this.$data)
    // 4. Call the Compiler object to parse the instructions and difference expressions
    new Compiler(this)}_proxyData(data) {
    Object.keys(data).forEach(key= > {
      Object.defineProperty(this, key, {
        enumerable: true.configurable: true.get() {
          return data[key]
        },
        set(newValue) {
          if(newValue ! == data[key]) { data[key] = newValue } } }) }) } }Copy the code

2.2 the Observer

  • function
    • Is responsible for converting the properties in the Data option to responsive data
    • A property in data is also an object, and that property is converted to reactive data
    • Data change notification is sent
  • implementation

Here we need to note that in get we return val instead of data[key], because using data[key] triggers the getter and becomes an infinite loop.

In addition, we modify val in set, and get returns val. Val is locked in defineReactive, so we can modify val in both get and set

class Observer {
  constructor (data) {
    this.walk(data)
  }

  walk(data) {
    // 1. Check whether data is an object
    if(! data ||typeofdata ! = ='object') {
      return
    }
    Object.keys(data).forEach(key= > {
      this.defineReactive(data, key, data[key])
    })
  }

  defineReactive(data, key, val) {
    let _self = this
    let dep = new Dep()
    this.walk(val)
    Object.defineProperty(data, key, {
      enumerable: true.configurable: true.get() {
        Dep.target && dep.addSub(Dep.target) // The observer mode adds an observer
        return val
      },
      set(newValue) {
        if(newValue ! == val) { val = newValue _self.walk(newValue) dep.notify()// The observer mode publishes notifications}}}Copy the code

3.3 Dep

  • function
    • Collect dependencies, add Watcher
    • Inform all observers
  • implementation

class Dep {
  constructor() {
    this.subs = []
  }

  addSub(sub) {
    if(sub && sub.update) {
      this.subs.push(sub)
    }
  }

  notify() {
    this.subs.forEach(sub= > {
      sub.update()
    })
  }
  
  remove() {
    if(arr.length) {
      const index = arr.indexOf(item)
      if(index > -1) {
        return arr.splice(index, 1)}}}}Copy the code

3.4 watcher

  • function
    • When data changes trigger dependencies, DEP informs all Watcher instances to update the view
    • Adds itself to the DEP object when it instantiates itself
  • implementation

We set dep. target = this, Then call this.oldValue = VM [key], which triggers a getter, i.e. the get method in defineReactive does dep.target && dep.addsub (dep.target). Add an observer, and then we empty target dep. target = null

class Watcher { 
  constructor (vm, key, cb) { 
    this.vm = vm 
    // Attribute name in data
    this.key = key 
    // Call cb to update the view when the data changes
    this.cb = cb 
    // Record the current watcher object on the static property of the Dep and add the watcher to the SUBs of the Dep when data is accessed
    Dep.target = this 
    // Trigger a getter that tells DEp to record watcher for the current key
    this.oldValue = vm[key] 
    / / clear the target
    Dep.target = null 
  }
  update () { 
    const newValue = this.vm[this.key] 
    if (this.oldValue === newValue) { 
      return 
    }
    this.cb(newValue) 
  } 
}
Copy the code

3.5 call watcher

Create a watcher object in compiler.js for each instruction/interpolation to monitor data changes

// because it is used in textUpdater, etc
this updaterFn && updaterFn.call(this, node, this.vm[key], key) 

// Update method of the v-text directive
textUpdater (node, value, key) { 
  node.textContent = value // Create a watcher for each instruction to watch the data change
  new Watcher(this.vm, key, value= > { 
    node.textContent = value 
  }) 
}
Copy the code

View changes update data

// Update method of the V-model directive
modelUpdater (node, value, key) { 
  node.value = value // Create a watcher for each instruction to watch the data change
  new Watcher(this.vm, key, value= > { 
  	node.value = value 
  })// Listen for changes to the view
  node.addEventListener('input'.() = > { 
  	this.vm[key] = node.value 
  }) 
}
Copy the code

3.6 the batch

Function:

  • Handle view rendering asynchronously, so that when we modify data multiple times, we only render the last time
  • Because of asynchronous rendering, the data we get here must be the last data.
  • We can conclude that the modification data is synchronous and the rendering page is asynchronous
class Batcher {
  constructor () {
    this.has = {}
    this.queue = []
    this.waiting = false
  }

  push(job) {
    let id = job.id
    // If there is no id, proceed to the next step
    if (!this.has[id]) {
      this.queue.push(job)
      // Set the ID of the element
      this.has[id] = true
      if (!this.waiting) {
        this.waiting = true
        if ("Promise" in window) {
          Promise.resolve().then( () = > {
            this.flush()
          })
        } else {
          setTimeout(() = > {
            this.flush()
          }, 0)}}}}flush() {
    this.queue.forEach((job) = > {
      job.cb(job.newValue)
    })
    this.reset()
  }

  reset() {
    this.has = {}
    this.queue = []
    this.waiting = false}}Copy the code

3.7 Overall Process

Source: gitee.com/zxhnext/fed…

4. Vue source MVVM

Let’s look at the source code to find answers to the following questions

  • vm.msg = { count: 0 }, re-assign a value to the property, is it responsive?
  • vm.arr[0] = 4To assign a value to an array element, whether the view will be updated
  • vm.arr.length = 0To change the length of the array and whether the view will be updated
  • vm.arr.push(4), whether the view will be updated

4.1 initState

In the Vue initialization phase, _init method performs, executes initState (vm) method, it is defined in SRC/core/instance/state. Js This will determine whether the data function, Call the methods () method after determining whether it has the same name as methods/props/

  • Order of execution:
    • props
    • methods
    • data
    • computed
    • watch
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
  // Check if data is a function and has the same name as methods/props/, and then call observer().
  initData(vm)
} else {
  observe(vm._data = {}, true /* asRootData */)}if (opts.computed) initComputed(vm, opts.computed)
if(opts.watch && opts.watch ! == nativeWatch) { initWatch(vm, opts.watch) }Copy the code

The initState method initializes the props, methods, data, computed, and Wathcer properties

There are two main things to do here: one is to iterate over the object returned by defining the data function, and proxy each value vm._data.xxx to vm.xxx. The other is to call the observe method to observe the entire data change, making data also responsive

4.2 Reactive objects

1. observer

The observer in/SRC/core/observer/index. Js

  • Check whether value is an object or vNode instance. If yes, return value directly
  • If the value is__ob__(Observer object) property, indicating that the object already exists
  • Create an Observer object
export function observe (value: any, asRootData: ? boolean) :Observer | void {
  // Check whether value is an object or a VNode instance
  if(! isObject(value) || valueinstanceof VNode) {
    return
  }
  let ob: Observer | void
  // If value has an __ob__(observer object) attribute, the object already exists
  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
    ob = value.__ob__
  } else if( shouldObserve && ! isServerRendering() && (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) && ! value._isVue// Cannot be a vue instance
  ) {
    // Create an Observer object
    ob = new Observer(value)
  }
  if (asRootData && ob) {
    ob.vmCount++
  }
  return ob
}
Copy the code

2. Observer

  • src\core\observer\index.js
    • Add an instance of itself to the data object value by executing the def function__ob__attribute
    • Reactive processing of the object
    • Responding to arrays (a separate section, which I’ll skip here)
    • walk(obj) 
      • Walk through all the properties of OBj, calling defineReactive() for each property, setting the getter/setter
export class Observer {
  constructor (value: any) {
    this.value = value
    this.dep = new Dep()
    // Initialize the instance vmCount to 0
    this.vmCount = 0
    // Mount the instance to the __ob__ attribute of the observed object
    def(value, '__ob__'.this)
    // Array response processing
    if (Array.isArray(value)) {
      if (hasProto) {
        protoAugment(value, arrayMethods)
      } else {
        copyAugment(value, arrayMethods, arrayKeys)
      }
      // Create an observer instance for each object in the array
      this.observeArray(value)
    } else {
      // Iterates over each property in the object, turning it into a setter/getter
      this.walk(value)
    }
  }

  walk (obj: Object) {
    // Get each property of the observed object
    const keys = Object.keys(obj)
    // Iterate over each attribute and set it to responsive data
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }

  observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }
}
Copy the code

3. defineReactive()

  • src\core\observer\index.js
  • defineReactive(obj, key, val, customSetter, shallow)
    • Define a responsive property for an object, each property corresponding to a DEP object
    • If the object is not configurable, do nothing
    • If the value of the property is an object, the call to Observe continues
    • If a new value is assigned to the property, the call to observe continues
    • Create dependencies in GET
    • Set sends notifications if data is updated
export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function, shallow? : boolean) {
  // Create an instance of the dependent object
  const dep = new Dep()
  // Get the property descriptor object for obj. If the object is not configurable, return it directly
  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }

  if((! getter || setter) &&arguments.length === 2) {
    val = obj[key]
  }
  // Determine whether to recursively observe the child object, and turn the child object properties into getters/setters, returning the child observation object
  letchildOb = ! shallow && observe(val)Object.defineProperty(obj, key, {
    enumerable: true.configurable: true.get: function reactiveGetter () {
      // Value equals the return value of the getter call if a predefined getter exists
      // Otherwise, give the attribute value directly
      const value = getter ? getter.call(obj) : val
      // Create a dependency if the current dependency target, a watcher object, exists
      if (Dep.target) {
        dep.depend()
        // If the subobject of observation exists, establish dependencies between the subobjects
        if (childOb) {
          childOb.dep.depend()
          // If the property is an array, the special treatment is to collect array object dependencies
          if (Array.isArray(value)) {
            dependArray(value)
          }
        }
      }
      // Return the attribute value
      return value
    },
    set: function reactiveSetter (newVal) {
      // Value equals the return value of the getter call if a predefined getter exists
      // Otherwise, give the attribute value directly
      const value = getter ? getter.call(obj) : val
      // Do not execute if the new value is equal to the old value or if the old value is NaN
      /* 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()
      }
      Return if there is no setter
      // #7981: for accessor properties without setter
      if(getter && ! setter)return
      // Call if the predefined setter exists, otherwise update the new value directly
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      // If the new value is an object, observe the child object and return the child observer objectchildOb = ! shallow && observe(newVal)// Distribute updates (publish change notices)
      dep.notify()
    }
  })
}
Copy the code

4.3 Collecting Dependencies

1. dep

  • Dep is actually a kind of management for Watcher
  • addSub
  • notify
  • Dep.targetOnly one watcher can be used at a time
export default class Dep {
  // Static property, watcher object
  statictarget: ? Watcher;// Dep Instance Id
  id: number;
  // Watcher object/subscriber array corresponding to the dep instance
  subs: Array<Watcher>;

  constructor () {
    this.id = uid++
    this.subs = []
  }

  // Add a new subscriber watcher object
  addSub (sub: Watcher) {
    this.subs.push(sub)
  }


  // Create dependencies between the observed objects and the watcher
  depend () {
    if (Dep.target) {
      // If the target exists, add the dep object to the watcher dependency
      Dep.target.addDep(this)}}// Issue a notification
  notify () {
    const subs = this.subs.slice()
    // Call each subscriber's update method to implement the update
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}

// dep. target is used to store the watcher currently in use
// Only one watcher can be used at a time
Dep.target = null
const targetStack = []
// Push the stack and assign the current watcher to dep.target
// When nesting parent components, first push the watcher corresponding to the parent component.
// Remove the watcher of the parent component from the stack and continue
export function pushTarget (target: ? Watcher) {
  targetStack.push(target)
  Dep.target = target
}

export function popTarget () {
  // Exit the stack
  targetStack.pop()
  Dep.target = targetStack[targetStack.length - 1]}Copy the code

2. watcher

  • get
    • PushTarget Pushes the stack and assigns the current watcher to dep. target
    • Value = this.getter.call(VM, VM) // Read value, trigger get
    • PopTarget: The popTarget is the next target assigned to the stack
  • update
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 "The ${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
}

update () {
  /* istanbul ignore else */
  if (this.lazy) {
    this.dirty = true
  } else if (this.sync) {
    this.run()
  } else {
    queueWatcher(this)}}Copy the code

3. Register watcher

updateComponent = () = > {
  vm._update(vm._render(), hydrating)
}
new Watcher(vm, updateComponent, noop, {
  before () {
    if (vm._isMounted) {
      callHook(vm, 'beforeUpdate')}}},true /* isRenderWatcher */)
Copy the code

4.4 Array Processing

1. Get the array prototype arrayMethods

Create a variable called arrayMethods, which inherits from Array.prototype and has all of its functionality. In the future, we’ll use arrayMethods to override Array.prototype.

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto) ; ['push'.'pop'.'shift'.'unshift'.'splice'.'sort'.'reverse'
].forEach(function(method) {
  // Cache the original method
  const original = arrayProto[method]
  Object.defineProperty(arrayMethods, method, {
    value:function mutator(. args) {
      return original.apply(this, args)
    },
    enumerable:false.writable:true.configurable: true})})Copy the code

2. Cover responsive dataArray.prototype

We override array. prototype with arrayMethods. While we can’t override directly because that would pollute the global Array, we want the interceptor to override only the data whose changes are detected. That is, we want the interceptor to override only the prototype of the responsive Array. Turning a data into reactive data requires an Observer, so we only need to use interceptors in the Observer to override the prototypes that are going to be converted into reactive Array data

export class Observer {
  constructor(value){
    this.value = value
    if(Array.isArray(value)){
      value.__proto__ = arrayMethods/ / new
    } else {
      this.walk(value)
    }
  }
}
Copy the code

__proto__ = arrayMethods is used to assign an interceptor (an arrayMethods that has been processed to intercept) to value.__proto__ is a clever way to override the value stereotype

3. No__proto__In the case

When __proto__ cannot be used, Vue does a pretty rough job of simply setting arrayMethods methods on the array being detected if __proto__ cannot be used

//_proto_ Whether available
const hasProto = '__proto__' in {}
const arrayKeys = Object.getOMnPropertyNames(arrayMethods)
export class Observer {
  constructor(value){
    this.value = value
    if (Array.isArray(value)) {
      / / modify
      const augment = hasProto ? protoAugment : copyAugment
      augment(value, arrayMethods, arrayKeys)
    } else {
      this.walk(value)
    }
  }
}

function protoAugment(target, src, keys) {
  target.__proto__ = src
}
function copyAugment(target, src, keys) {
  for(let i=0,l = keys.length; i<l; i++) {
    const key = keys[i]
    def(target,key,src[key])
  }
}
Copy the code

4. Add an array__ob__

__ob__ is used not only to access the Observer instance in the interceptor, but also to indicate whether the current value has been converted to responsive data by the Observer. That is, all data whose changes are detected will have an __ob__ attribute to indicate that they are responsive. If value is responsive, __ob__ is returned; If it is not responsive, the new Observer is used to transform the data into responsive data. When the value is marked __ob__, the observer instance can be accessed through the value. In the case of an Array interceptor, because the interceptor is a prototype method, the Observer instance can be accessed directly through this.__ob__

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto) ; ['push'.'pop'.'shift'.'unshift'.'splice'.'sort'.'reverse'
].forEach(function(method) {
  // Cache the original method
  const original= arrayProto[method]
  def(arrayMethods,method, function mutator(. args){
    const result = original.apply(this,args)
    const ob = this.__ob__
    ob.dep.notify() // Send a message to the dependency
    return result
  })
})
Copy the code

5. Detects array element changes

export class Observer {
  constructor(value){
    this.value = value
    def(value, '__ob__'.this)
    if (Array.isArray(value)) {
      / / modify
      const augment = hasProto ? protoAugment : copyAugment
      augment(value, arrayMethods, arrayKeys)
      this.observerArray(value)
    } else {
      this.walk(value)
    }
  }
  
  observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }
}
Copy the code

6. Listen for new elements in the array

; ['push'.'pop'.'shift'.'unshift'.'splice'.'sort'.'reverse'
].forEach(function(method) {
  // Cache the 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.observerArray(inserted)
    ob.dep.notify() // Send a message to the dependency
    return result
  })
})
Copy the code

If method is push, unshift, or splice, then the new element is removed from args and placed in inserted. Next, an Observer is used to turn the elements in inserted into responsive.

4.5 Update the render queue asynchronously

  • The has object is guaranteed to be added to the same Watcher only once

  • Flushing indicates whether the queue is being processed, adding directly to the queue if not, or inserting into the queue otherwise

  • Waiting indicates whether the current queue is executing. If not, call flushSchedulerQueue, ensuring that nextTick(flushSchedulerQueue) is called only once

  • Purpose of queue

      1. Components are updated in order from parent to child, because the parent component is created before the child component is created
      1. User custom Watcher is executed before render watcher; Because the user custom Watcher is created before the watcher is rendered
      1. If a component is destroyed during the execution of the parent’s watcher, its watcher execution can be skipped, so the parent’s watcher should be executed first
  • Traverse the watcher

    • Do not cache the queue length because the queue is added dynamically
export function queueWatcher (watcher: Watcher) {
  const id = watcher.id
  if (has[id] == null) {
    has[id] = true
    // Whether the queue is being processed
    if(! flushing) {// Add to queue
      queue.push(watcher)
    } else {
      // if already flushing, splice the watcher based on its id
      // if already past its id, it will be run next immediately.
      let i = queue.length - 1
      while (i > index && queue[i].id > watcher.id) {
        i--
      }
      queue.splice(i + 1.0, watcher)
    }
    // queue the flush
    if(! waiting) { waiting =true

      if(process.env.NODE_ENV ! = ='production' && !config.async) {
        flushSchedulerQueue()
        return
      }
      nextTick(flushSchedulerQueue)
    }
  }
}


function flushSchedulerQueue () {
  currentFlushTimestamp = getNow()
  flushing = true
  let watcher, id
  // The purpose of sorting
  // 1. The component update sequence is from parent to child, because the parent component is created first and then the child component is created
  // 2. The component's user watcher should run before rendering the watcher
  // 3. If a parent component is destroyed before a component is run, skip this update
  queue.sort((a, b) = > a.id - b.id)

  // do not cache length because more watchers might be pushed
  // as we run existing watchers
  // Do not cache the queue length because it is added dynamically
  for (index = 0; index < queue.length; index++) {
    watcher = queue[index]
    if (watcher.before) {
      watcher.before()
    }
    id = watcher.id
    has[id] = null
    watcher.run()
  }

  // keep copies of post queues before resetting state
  const activatedQueue = activatedChildren.slice()
  const updatedQueue = queue.slice()

  resetSchedulerState()

  // call component updated and activated hooks
  callActivatedHooks(activatedQueue)
  callUpdatedHooks(updatedQueue)

  // devtool hook
  /* istanbul ignore if */
  if (devtools && config.devtools) {
    devtools.emit('flush')}}Copy the code

5. Schematic diagram