Vue asynchronous update mechanism

Dom updates in VUE are performed asynchronously, and as long as you listen for data changes, vUE opens a queue and buffers all data changes that occur in the same event loop. If the same watcher is triggered more than once, it will only be pushed into the queue once. Then, in the next event loop, “Tick,” Vue refreshes the queue and performs the actual (de-duplicated) work (first buffering, then queuing the last of the multiple firing properties as a promise; Instead of putting it in a queue)

The data in vUE is updated to DOM asynchronously. The asynchronous update of data can be understood as a promise microtask. And when multiple assignments are made to the same attribute value, only the last assignment is placed on the update queue as a Promise microtask.

Note that the nextTick in the event loop may be executed during the current Tick microtask execution phase or the nextTick, depending on whether the nextTick function uses Promise/MutationObserver or setTimeout

Conclusion of Scenario 1: DOM updates asynchronously, which is equivalent to a Promise microtask

// Scenario 1:<div id="example">{{message}}</div>
<script>
  var vm = new Vue({
    el: '#example'.data: {
      message: '123'}})// When you encounter this sentence, interpret it as a promise and put it in the promise queue
  vm.message = 'new message' // Change the data

  Promise.resolve(100).then(value= > {
    console.log("If the assignment above was equivalent to a promise, the output would be New Message.", vm.$el.textContent);		// Step 2 output: new message
  })
  console.log(vm.$el.textContent);	// First step output: 123
</script>

Copy the code

Scenario 2 Conclusion: Assignment changes the response property as a promise and executes in the same order as a normal promise queue

// Example 1:
var vm = new Vue({
  el: '#example'.data: {
    message: '123'
  }
})

vm.message = 'new' // Change the data

Promise.resolve(100).then(value= > {
  console.log("Test first assignment", vm.$el.textContent);		Vm. message = 'new' is put in the promise queue first, so the output of this line is changed to new.
})
console.log(vm.$el.textContent);		// First step output: 123




// Example 2:
var vm = new Vue({
  el: '#example'.data: {
    message: '123'}})Promise.resolve(100).then(value= > {
  console.log("Test first assignment", vm.$el.textContent);		The promise vm. Message = 'new' has not been executed yet, so the output is still 123.
})

vm.message = 'new' // Change the data
console.log(vm.$el.textContent);		// First step output: 123
Copy the code

Scenario 3 Conclusion: Scenario 3 illustrates the execution sequence of asynchronous updates and the principles summarized below

// Example 1:<div id="example">{{message}}</div>
<script>
  var vm = new Vue({
    el: '#example'.data: {
      message: '123'
    }
  })

  vm.message = 'msg' // Change the data

  Promise.resolve(100).then(value= > {
    console.log("Test first assignment", vm.$el.textContent);		// Step 2 output: new
  })

  vm.message = 'new' // Change the data
  console.log("= =", vm.$el.textContent);		// Step 1 123

  Promise.resolve(100).then(value= > {
    console.log("Test the second assignment", vm.$el.textContent);		// Step 3 output: new
  })
</script>// Example 2:<div id="example">{{message}}</div>
<script>
  var vm = new Vue({
    el: '#example'.data: {
      message: '123'}})Promise.resolve(1).then(value= > {
    console.log("xxx", vm.$el.textContent);		// Step 2 output: 123
  })

  vm.message = 'msg' // Change the data

  Promise.resolve(2).then(value= > {
    console.log("Test", vm.$el.textContent);  // Step 3 output: new
  })

  vm.message = 'new' // Change the data

  Promise.resolve(3).then(value= > {
    console.log("fff", vm.$el.textContent);			// Step 4 output: new
  })
  
  console.log(vm.$el.textContent);			// First step output: 123
</script>// Conclusion: Examples 1 and 2 in scenario 3 illustrate the vUE asynchronous update principle. Using example 2 of scenario 3, the flow is briefly described: The main process executes the code when it encounters THE promise1, places the promise1 first in the microtask queue, then the main process continues to execute the synchronization code when it encounters vm.message = 'MSG' and places the vm.message = 'MSG' as a promise in the task queue second. The main thread continues to execute the synchronization code and finds promise2, puts promise2 in the third bit of the microtask queue, and hits Vm. message = 'new'. If vm. Message = 'new' is already in the buffer, then the main thread continues to execute the synchronization code and encounters the promise3, Place promise3 on the fourth bit of the microtask queue, and the main thread continues to execute the synchronization code and hits console.log(vm.$el.textContent); If the command is executed directly, 123 is displayed in the first step. Vm. message = 'MSG'; vm.message = 'new'; vm.message = 'MSG'; vm.message = 'new'; The first task "promise1" (" xxx123 ") and the second task "vm.message = 'new'" are rendered and updated to the dom node of div#example. The third task, promise2, outputs a test new (because the previous task has updated new to the DOM node). The fourth task promise3 outputs FFF new. The main point to note is that VUE will queue the asynchronous task updating data according to the synchronous code execution of the main thread normally, but will cache the attributes in the buffer. If the synchronization code is encountered again, the attribute value of the buffer will be updated, but will not queue as a task when it is encountered again. Finally, when the synchronous code is finished, the final value of the buffer is updated to the asynchronous data update task in the microtask queue, and the microtask queue starts to execute from beginning to end. Finally, a successful conclusion.Copy the code

Vue.nextTick(callback)

1. Introduction

There is no way to update the DOM immediately after the vm.message = ‘MSG’ // change the data, because the change is a promise. We can force DOM updates using vue.nexttick (callback) if we want to do something after changing the data and before any other synchronization code.

Segmentfault.com/a/119000001…

<! DOCTYPEhtml>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
    <script src="https:cdn.jsdelivr.net/npm/vue"></script>
</head>

<body>

    <div id="example"></div>
    <script>
        var vm = new Vue({
            el: '#example'.data: {
                message: '111'
            },
            beforeMount() {
                console.log("beforeMount");
            },
            mounted() {
                console.log("mounted");
            },
            beforeUpdate() {
                console.log("beforeUpdate");
            },
            updated() {
                console.log("updated");
            },
            render: function (createElement, context) {
                console.log("render");
                return createElement(
                    'div', {},this.message] ); }})setTimeout(() = > {
            console.log("setTimeout");
        }, 0)
        Promise.resolve().then(value= > {
            console.log("Promise1", vm.$el.textContent);
            Vue.nextTick(() = > {
                vm.message = '555'
                Vue.nextTick(() = > {
                    console.log("promise1==InNextTick===", vm.$el.textContent);
                })
                console.log("promise1==nextTick===", vm.$el.textContent);
            })
        })

        Vue.nextTick(() = > {
            console.log("topnextTick===", vm.$el.textContent);
        })

        vm.message = '222' // Change the data

        Promise.resolve().then(value= > {
            console.log("Promise2", vm.$el.textContent);
            Vue.nextTick(() = > {
                console.log("promise2==nextTick===", vm.$el.textContent);
            })
        })

        Vue.nextTick(() = > {
            // vm.message = '555'
            Vue.nextTick(() = > {
                console.log("bottomInnerNextTick===", vm.$el.textContent);
            })
            console.log("bottomnextTick===", vm.$el.textContent);
        })

        vm.message = '333' // Change the data

        Promise.resolve().then(value= > {
            console.log("Promise3", vm.$el.textContent);
        })

        console.log(vm.$el.textContent);

        vm.message = '444'

    </script>
</body>

</html>
Copy the code

2. To summarize

Premise: With Vue2.6, nextTick is treated as a promise

  1. Vm. XXX = XXX is treated as a promise. See juejin.cn/post/688272…

  2. XXX = XXX triggers the updated update, and the nextTick in the current microtask queue will be executed immediately after the update

Xx = XXX, P1 (P1-nextTICK), p2, nextTick1, p3, nextTick2.P1 (p1-NextTICK) : p1 This Promise callback also contains a nextTick. P2: p2 is a Promise callback. NextTick1: a nextTick callback. P3: P3 is a Promise callback. A nextTick callback is executed sequentially`vm.xx=xxx`Trigger beforeUpdate, render, updated, then nextTick1 callback, then nextTick2 callback, then P1 callback, and add P1-Nexttick in THE P1 callback to the end of the microtask queue after P3. Execute p2, execute p3, and execute p1-nexttickCopy the code

Render position

Conclusion:

  • Component mounting dimensions: beforeMount, Render, and Mounted
  • Data update dimensions: beforeUpdate, Render, updated
<div id="example">{{message}}</div>
<script>
  var vm = new Vue({
    el: '#example'.data: {
      message: '111'
    },

    beforeMount() {
      console.log("beforeMount");

    },
    mounted() {
      console.log("mounted");
    },
    beforeUpdate() {
      console.log("beforeUpdate");
    },
    updated() {
      console.log("updated");
    },
    render: function (createElement, context) {
      console.log("render");
      return createElement(
        'div',
        {
          class: { 'my-class': true}},this.message] ); }})</script>
Copy the code

The relationship between task and render

Macro task queue: A B

Microtask queues: A, B, and C

The execution sequence is: first load macro task A to the execution stack, then execute the three micro tasks A, B and C, then execute render rendering page, and then load macro task B to the execution stack. (Note that script, the synchronized code that starts out on the main execution stack, is also macroTask in nature, the first task to execute.)

Conclusion: The microtask queue is executed before render, and the browser’s Render UI rendering is inserted between each MacroTask