My first comment is to prevent child components from rendering repeatedly

Take a look at the very simple code below

App.vue

<template> <div id="app"> <div> a:{{ a }} <HelloWorld1 :a="a" @changea="changea"> </HelloWorld1> <HelloWorld2 :a="a"> </HelloWorld2> </div> </div> </template> <script> import HelloWorld1 from "./components/HelloWorld1.vue"; import HelloWorld2 from "./components/HelloWorld2.vue"; export default { name: "App", components: { HelloWorld1, HelloWorld2, }, data() { return { a: 0, }; }, updated() { console.log("APP update"); }, methods: { changea() { debugger; this.a++; ,}}}; </script>Copy the code

HelloWorld1.vue

<template> <div> <button @click="click">Change b</button> <div>HelloWorld1-a:{{ a }}</div> <div>HelloWorld1-b:{{ b }}</div> </div> </template> <script> export default { name: "HelloWorld1", props: ["a"], data() { return { b: 0, }; }, updated() { console.log("HelloWorld1 update"); }, methods: { click() { debugger; this.b = this.b + 1; this.$emit("changea", this.b); ,}}}; </script>Copy the code

Helloword2.vue

<template> <div>HelloWorld2-a:{{ a }}</div> </template> <script> export default { name: "HelloWorld2", props: ["a"], data() { return {}; }, methods: { click() { debugger; this.a++; ,}}}; </script>Copy the code

1.Helloword1 and Helloword2 are child components of App

2. Click the ‘Change B ‘button of Helloword1, the b in Helloword1 component will +1, and send the updated B to parent App through emit(“changea”,this.b) to Change the A of App.

3.A of App is passed to both Helloword1 and Helloword2 as prop.

The above process can be summarized as three things that happen directly or indirectly when a button is pressed.

  1. Changing B directly triggers the Helloword1 rendering watcher
  2. B causes A to change and A triggers the App’s rendering watcher
  3. When the App renders watcher and updateChildComponent updates the child component, it also triggers the child component “props[a]” (props[key] = validateProp(key, propOptions, propsData, vm);So Helloword1 and Helloword2 will perform their respective render watcher

A few points to make when I say the following (I’m not talking about computed and Watcher here, assuming that all components have only rendered Watcher)

1. Watcher does not perform rendering immediately, but maintains them in a queue for the next microtask (most) to execute

2. If the execution of the queue (in this example can be understood as a component of a render watcher is executing), if again trigger other component rendering function * * (assuming the b component), at this time will be b component and a component of rendering watcher id, if b is bigger, is directly into the queue of the line, Otherwise, it will wait for the next microtask

3. If a watcher with the same ID is encountered during queue insertion, only one watcher with the same ID is left

FlushSchedulerQueue is the method that executes the watcher rendering in the queue in turn

Now assume (I’m debugger out with these ids, but whatever the ID is, the size of the ID is the parent component > the child component)

The App component’s render Watcher id is 2

The render Watcher for the Helloword1 component has id 3

The render watcher for the Helloword2 component has id 4

Suppose the flushSchedulerQueue does not sort the Watcher queue from smallest to largest

Comment sort code// queue.sort(function (a, b) { return a.id - b.id; });
Copy the code
  • Change b to directly trigger Helloword1’s rendering watcher, queue to [3]

  • B causes a to change, a triggers the render watcher of App,queue is [3,2].

  • FlushSchedulerQueue executes in [3,2] order

First, the Helloword1 render watcher (id=3) completes (execute once), and the queue considers that there is no more watcher with this id.

Next, the App’s render Watcher (id=2) executes, triggering the Render Watcher for HelloWorld1 and HelloWorld2

So queue will push into two render Watcher with id=3 and id=4, so render Watcher with id=3 and id=4 will execute (execute second)

As you can see from above, the Watcher with ID =3 executes both sides, meaning that HellowWorld1’s component is updated twice in a single button click

FlushSchedulerQueue Sorts the Watcher queue from smallest to largest

  • Change b to directly trigger Helloword1’s rendering watcher, queue to [3]

  • B causes a to change, a triggers the render watcher of App,queue is [3,2].

  • FlushSchedulerQueue is executed, and ids are sorted from smallest to largest in order [2,3]

First, the App’s render Watcher (id=2) executes, triggering the Render Watcher for HelloWorld1 and HelloWorld2

And then the queue already has a watcher with id=3, so instead of pushing the render watcher with id=3, it just pushes the render watcher with id=4, and now the queue has [2,3,4]

As you can see from the above, watcher with id=3 executes only once