Home Web Front-end JS Tutorial Detailed explanation of the use of Vue nextTick mechanism

Detailed explanation of the use of Vue nextTick mechanism

May 15, 2018 am 09:19 AM
nexttick mechanism Detailed explanation

This time I will bring you a detailed explanation of the use of the Vue nextTick mechanism. What are the precautions when using the Vue nextTick mechanism. The following is a practical case, let's take a look.

Let’s first look at a piece of Vue execution code:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

export default {

 data () {

  return {

   msg: 0

  }

 },

 mounted () {

  this.msg = 1

  this.msg = 2

  this.msg = 3

 },

 watch: {

  msg () {

   console.log(this.msg)

  }

 }

}

Copy after login

We guess that after executing this script for 1000m, it will print: 1, 2, 3. But in actual effect, it will only be output once: 3. Why is there such a situation? Let’s find out.

queueWatcher

We define watch to listen to msg, which will actually be called by Vue like vm.$watch(keyOrFn, handler, options). $watch is a function bound to vm when we initialize it, used to create Watcher objects. Then let's take a look at how the handler is handled in Watcher:

1

2

3

4

5

6

7

8

9

10

11

12

this.deep = this.user = this.lazy = this.sync = false

...

 update () {

  if (this.lazy) {

   this.dirty = true

  else if (this.sync) {

   this.run()

  else {

   queueWatcher(this)

  }

 }

...

Copy after login

Initial setting this.deep = this.user = this.lazy = this.sync = false, that is, when an update is triggered, To execute the queueWatcher method:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

const queue: Array<Watcher> = []

let has: { [key: number]: ?true } = {}

let waiting = false

let flushing = false

...

export function queueWatcher (watcher: Watcher) {

 const id = watcher.id

 if (has[id] == null) {

  has[id] = true

  if (!flushing) {

   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

   nextTick(flushSchedulerQueue)

  }

 }

}

Copy after login

The flushSchedulerQueue function in nextTick(flushSchedulerQueue) is actually the watcher's ViewUpdate:

1

2

3

4

5

6

7

8

9

10

11

12

function flushSchedulerQueue () {

 flushing = true

 let watcher, id

 ...

 for (index = 0; index < queue.length; index++) {

  watcher = queue[index]

  id = watcher.id

  has[id] = null

  watcher.run()

  ...

 }

}

Copy after login

In addition, regarding the waiting variable, this It is a very important flag, which ensures that the flushSchedulerQueue callback is only allowed to be placed in callbacks once. Next, let's take a look at the nextTick function. Before talking about nexTick, you need to have a certain understanding of Event Loop, microTask, and macroTask. Vue nextTick also mainly uses these basic principles. If you don’t understand it yet, you can refer to my article Introduction to Event Loop. Now let’s take a look at its implementation:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

export const nextTick = (function () {

 const callbacks = []

 let pending = false

 let timerFunc

 function nextTickHandler () {

  pending = false

  const copies = callbacks.slice(0)

  callbacks.length = 0

  for (let i = 0; i < copies.length; i++) {

   copies[i]()

  }

 }

 // An asynchronous deferring mechanism.

 // In pre 2.4, we used to use microtasks (Promise/MutationObserver)

 // but microtasks actually has too high a priority and fires in between

 // supposedly sequential events (e.g. #4521, #6690) or even between

 // bubbling of the same event (#6566). Technically setImmediate should be

 // the ideal choice, but it&#39;s not available everywhere; and the only polyfill

 // that consistently queues the callback after all DOM events triggered in the

 // same loop is by using MessageChannel.

 /* istanbul ignore if */

 if (typeof setImmediate !== &#39;undefined&#39; && isNative(setImmediate)) {

  timerFunc = () => {

   setImmediate(nextTickHandler)

  }

 else if (typeof MessageChannel !== 'undefined' && (

  isNative(MessageChannel) ||

  // PhantomJS

  MessageChannel.toString() === '[object MessageChannelConstructor]'

 )) {

  const channel = new MessageChannel()

  const port = channel.port2

  channel.port1.onmessage = nextTickHandler

  timerFunc = () => {

   port.postMessage(1)

  }

 else

 /* istanbul ignore next */

 if (typeof Promise !== 'undefined' && isNative(Promise)) {

  // use microtask in non-DOM environments, e.g. Weex

  const p = Promise.resolve()

  timerFunc = () => {

   p.then(nextTickHandler)

  }

 else {

  // fallback to setTimeout

  timerFunc = () => {

   setTimeout(nextTickHandler, 0)

  }

 }

 return function queueNextTick (cb?: Function, ctx?: Object) {

  let _resolve

  callbacks.push(() => {

   if (cb) {

    try {

     cb.call(ctx)

    catch (e) {

     handleError(e, ctx, 'nextTick')

    }

   else if (_resolve) {

    _resolve(ctx)

   }

  })

  if (!pending) {

   pending = true

   timerFunc()

  }

  // $flow-disable-line

  if (!cb && typeof Promise !== 'undefined') {

   return new Promise((resolve, reject) => {

    _resolve = resolve

   })

  }

 }

})()

Copy after login

First, Vue simulates the event queue through callback array , events in the event team are called through the nextTickHandler method, and what is executed is determined by timerFunc. Let's take a look at the definition of timeFunc:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {

  timerFunc = () => {

   setImmediate(nextTickHandler)

  }

 else if (typeof MessageChannel !== 'undefined' && (

  isNative(MessageChannel) ||

  // PhantomJS

  MessageChannel.toString() === '[object MessageChannelConstructor]'

 )) {

  const channel = new MessageChannel()

  const port = channel.port2

  channel.port1.onmessage = nextTickHandler

  timerFunc = () => {

   port.postMessage(1)

  }

 else

 /* istanbul ignore next */

 if (typeof Promise !== 'undefined' && isNative(Promise)) {

  // use microtask in non-DOM environments, e.g. Weex

  const p = Promise.resolve()

  timerFunc = () => {

   p.then(nextTickHandler)

  }

 else {

  // fallback to setTimeout

  timerFunc = () => {

   setTimeout(nextTickHandler, 0)

  }

 }

Copy after login

You can see the priority of definition of timerFunc macroTask --> microTask, in an environment without Dom, use microTask, such as weex

setImmediate, MessageChannel VS setTimeout

We define setImmediate and MessageChannel first. Why should we use them first to create macroTask instead of setTimeout? HTML5 stipulates that the minimum time delay of setTimeout is 4ms, which means that under ideal circumstances, the fastest asynchronous callback can trigger is 4ms. Vue uses so many functions to simulate asynchronous tasks, with only one purpose, which is to make the callback asynchronous and called as early as possible. The delays of MessageChannel and setImmediate are obviously smaller than setTimeout.

Solution to the problem

With these foundations in mind, let’s look at the problems mentioned above again. Because Vue's event mechanism schedules execution through the event queue, it will wait for the main process to be idle before scheduling, so go back and wait for all processes to complete before updating again. This kind of performance advantage is obvious, for example:

Now there is a situation where the value of test will be looped executed 1000 times when mounted. Each time, setter->Dep->Watcher->update->run will be triggered responsively. If the view is not updated asynchronously at this time, the DOM will be directly manipulated to update the view every time, which is very performance consuming. Therefore, Vue implements a queue, and the run of the Watcher in the queue will be executed uniformly on the next Tick (or the microtask phase of the current Tick). At the same time, Watchers with the same ID will not be added to the queue repeatedly, so the Watcher run will not be executed 1,000 times. The final update of the view will only directly change the DOM corresponding to test from 0 to 1000. It is guaranteed that the action of updating the view to operate the DOM is called at the next Tick (or the microtask phase of the current Tick) after the current stack is executed, which greatly optimizes performance.

Interesting question

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

var vm = new Vue({

  el: '#example',

  data: {

    msg: 'begin',

  },

  mounted () {

   this.msg = 'end'

   console.log('1')

   setTimeout(() => { // macroTask

     console.log('3')

   }, 0)

   Promise.resolve().then(function () { //microTask

    console.log('promise!')

   })

   this.$nextTick(function () {

    console.log('2')

   })

 }

})

Copy after login

Everyone must know the execution order of this and print it in sequence: 1, promise, 2, 3.

  1. Because this.msg = 'end' is triggered first, the watcher's update is triggered, thereby pushing the update operation callback into the vue event queue.

  2. this.$nextTick also enters a new callback function for event queue push. They all come through setImmediate --> MessageChannel --> Promise --> setTimeout Define timeFunc. Promise.resolve().then is a microTask, so it will print the promise first.

  3. When MessageChannel and setImmediate are supported, their execution order takes precedence over setTimeout (in IE11/Edge, the setImmediate delay can be within 1ms, while setTimeout has a minimum delay of 4ms, so setImmediate executes the callback function earlier than setTimeout(0). Secondly, because in the event queue, the callback array is received first), so it will print 2, and then print 3

  4. but In the case where MessageChannel and setImmediate are not supported, timeFunc will be defined through Promise, and the old version of Vue before 2.4 will execute promise first. This situation will cause the order to become: 1, 2, promise, 3. Because this.msg must first trigger the dom update function, the dom update function will first be collected by the callback into the asynchronous time queue, and then Promise.resolve().then(function () { console.log('promise!')} will be defined. ) such a microTask, and then defining $nextTick will be collected by the callback. We know that the queue satisfies the first-in-first-out principle, so the objects collected by the callback are executed first.

Postscript

If you are interested in Vue source code, you can come here: More interesting Vue convention source code explanations

I believe you have mastered the method after reading the case in this article. For more exciting information, please pay attention to other related articles on the php Chinese website!

Recommended reading:

JS to implement transparency gradient function

##jQuery traversal of XML nodes and attributes implementation steps

The above is the detailed content of Detailed explanation of the use of Vue nextTick mechanism. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
2 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
Repo: How To Revive Teammates
1 months ago By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: How To Get Giant Seeds
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Detailed explanation of obtaining administrator rights in Win11 Detailed explanation of obtaining administrator rights in Win11 Mar 08, 2024 pm 03:06 PM

Windows operating system is one of the most popular operating systems in the world, and its new version Win11 has attracted much attention. In the Win11 system, obtaining administrator rights is an important operation. Administrator rights allow users to perform more operations and settings on the system. This article will introduce in detail how to obtain administrator permissions in Win11 system and how to effectively manage permissions. In the Win11 system, administrator rights are divided into two types: local administrator and domain administrator. A local administrator has full administrative rights to the local computer

Detailed explanation of division operation in Oracle SQL Detailed explanation of division operation in Oracle SQL Mar 10, 2024 am 09:51 AM

Detailed explanation of division operation in OracleSQL In OracleSQL, division operation is a common and important mathematical operation, used to calculate the result of dividing two numbers. Division is often used in database queries, so understanding the division operation and its usage in OracleSQL is one of the essential skills for database developers. This article will discuss the relevant knowledge of division operations in OracleSQL in detail and provide specific code examples for readers' reference. 1. Division operation in OracleSQL

Detailed explanation of the role and usage of PHP modulo operator Detailed explanation of the role and usage of PHP modulo operator Mar 19, 2024 pm 04:33 PM

The modulo operator (%) in PHP is used to obtain the remainder of the division of two numbers. In this article, we will discuss the role and usage of the modulo operator in detail, and provide specific code examples to help readers better understand. 1. The role of the modulo operator In mathematics, when we divide an integer by another integer, we get a quotient and a remainder. For example, when we divide 10 by 3, the quotient is 3 and the remainder is 1. The modulo operator is used to obtain this remainder. 2. Usage of the modulo operator In PHP, use the % symbol to represent the modulus

Detailed analysis of C language learning route Detailed analysis of C language learning route Feb 18, 2024 am 10:38 AM

As a programming language widely used in the field of software development, C language is the first choice for many beginner programmers. Learning C language can not only help us establish the basic knowledge of programming, but also improve our problem-solving and thinking abilities. This article will introduce in detail a C language learning roadmap to help beginners better plan their learning process. 1. Learn basic grammar Before starting to learn C language, we first need to understand the basic grammar rules of C language. This includes variables and data types, operators, control statements (such as if statements,

Detailed explanation of the linux system call system() function Detailed explanation of the linux system call system() function Feb 22, 2024 pm 08:21 PM

Detailed explanation of Linux system call system() function System call is a very important part of the Linux operating system. It provides a way to interact with the system kernel. Among them, the system() function is one of the commonly used system call functions. This article will introduce the use of the system() function in detail and provide corresponding code examples. Basic Concepts of System Calls System calls are a way for user programs to interact with the operating system kernel. User programs request the operating system by calling system call functions

Detailed explanation of Linux curl command Detailed explanation of Linux curl command Feb 21, 2024 pm 10:33 PM

Detailed explanation of Linux's curl command Summary: curl is a powerful command line tool used for data communication with the server. This article will introduce the basic usage of the curl command and provide actual code examples to help readers better understand and apply the command. 1. What is curl? curl is a command line tool used to send and receive various network requests. It supports multiple protocols, such as HTTP, FTP, TELNET, etc., and provides rich functions, such as file upload, file download, data transmission, proxy

Learn more about Promise.resolve() Learn more about Promise.resolve() Feb 18, 2024 pm 07:13 PM

Detailed explanation of Promise.resolve() requires specific code examples. Promise is a mechanism in JavaScript for handling asynchronous operations. In actual development, it is often necessary to handle some asynchronous tasks that need to be executed in sequence, and the Promise.resolve() method is used to return a Promise object that has been fulfilled. Promise.resolve() is a static method of the Promise class, which accepts a

Detailed explanation of numpy version query method Detailed explanation of numpy version query method Jan 19, 2024 am 08:20 AM

Numpy is a Python scientific computing library that provides a wealth of array operation functions and tools. When upgrading the Numpy version, you need to query the current version to ensure compatibility. This article will introduce the method of Numpy version query in detail and provide specific code examples. Method 1: Use Python code to query the Numpy version. You can easily query the Numpy version using Python code. The following is the implementation method and sample code: importnumpyasnpprint(np

See all articles