A generic queue model

Ordinary queue model, can be seen everywhere in real life, canteen queue, first come first eat.

It has the following characteristics:

  1. It’s a sequential list
  2. You can play and join the team
  3. First in first out

Queue model for VUE

Three queue

Vue has three queues: component data update queue, component data update queue, component data update queue.

Three queues, all executed around the component data update before, during and after.

What are component data updates?

<template>
  <p>{{name}}</p>
</template>
<script lang="ts">
import { ref, defineComponent } from 'vue'
export default defineComponent({
  setup(){
    const name = ref("vue setup")
    return {
      name
    }
  }
})
</script>
Copy the code

In the above code, component data update refers to the process in which the component name value is updated and the DOM value is also updated (although the DOM value is updated, the UI is not updated because the JS thread is still running the code).

Two different queue models

In the three queues of VUE, there are two different queue models

  1. Component data before update (Pre queue), component data after update queue (Post queue) – First in, first out, no priority, no queue jumping, allowing recursion
  2. Component data update queue – Higher priority jobs are executed first, queue cutting is allowed, and recursion is allowed

Here’s a look at the component data update queue

Component data update queue

As shown in the figure, each Job has an attribute ID. A Job with a smaller ID is executed first and can jump the queue during the process.

So why do they do it this way? We break it down into a few questions to answer:

What is the id of the Job? If queue cutting is allowed, the Job with the smallest ID is executed first.

The Job ID is the UID attribute of the vUE component internal instance. Is a counter that does not repeat itself. The uid of the first component is 0, the uid of the second component is 1, and so on.

Jobs with small ids are executed first, which ensures that the parent component is always updated before the child component (because the parent component is created first and then the child component is created, and the child component may depend on the parent component’s data).

Child components can only be updated when the parent component data has been updated.

Imagine if the child component data update is performed first, and then if the parent component updates the data on which the child depends, the child component needs to perform another data update.

Therefore, data with a small ID is executed first to ensure data correctness and improve data update performance

This is also the order in which component data is updated as determined by the top-down one-way data flow.

What is recursion? When is recursion allowed?

As shown in the figure above, when a Job is being executed, it is added to the queue again. This situation is called recursion.

By default, jobs are not recursive.

Job recursion is allowed: component updated Job, watch callback (which is also a Job)

When do component updates recurse?

Here’s an example:

Define global properties:

const app = createApp(App)
app.config.globalProperties.$loading = {
  isLoading: ref(false),}Copy the code

The parent component:

<template>
  <div>
    <div>{{ $loading.isLoading.value }}</div>
    <button @click='add'>{{ count }}</button>
    <Children :count='count' />
  </div>
</template>
<script setup lang='ts'>
/ / omit the import
const count = ref(0)
function add() {
  count.value = count.value + 1
}
</script>
Copy the code

Child components:

<template>
  <div>
    <p>{{ count }}</p>
  </div>
</template>

<script lang='ts'>
import { ref, defineComponent, onUpdated } from 'vue'
export default defineComponent({
  props: {
    count: {
      type: Number.required: true}},watch: {
    count() {
      this.$loading.isLoading.value = !this.$loading.isLoading.value
    }
  }
})
</script>
Copy the code

There are two components, father and son. After clicking button, its data flow is as follows:

  1. Parent component count increases
  2. The property of the child component is modified, triggering watch and changing loading
  3. Loading is modified and the parent component updates the DOM

As we’ll see here, this example does not satisfy one-way data flow: while the parent component is updating data, the child component changes global properties, causing the parent component to need to update.

Therefore, the parent component needs to enter the data update queue again and perform the update again to ensure that the data is correct. This is recursion.

Too much breaking of one-way data flows can result in multiple recursive updates, which can lead to performance degradation

The Pre/Post queue

The Pre/Post queue, in fact, is a little bit more complicated than a normal queue

What is executed for this type of queue?

A few examples:

  1. The watch function takes flush, which defaults to pre, and adds the watch callback to the Pre queue. Flush, set to POST, is added to the POST queue
  2. Mounted specifies the interval to execute in the Post queue until the component is updated and the DOM is mounted

Why are there execute and wait queues?

The Job may be added to the queue several times during execution. When the waiting queue is changed to the execution queue, the Job can be deduplicated at last

When does it recurse?

<template>
  <div @click='add'>
    {{list}}
  </div>
</template>

<script setup>
import { reactive,watch } from 'vue'

const list = reactive([])
watch(list, () = >{
  if(list.length < 10){
    list.push(1)}})function add(){
  list.push(1)}</script>
Copy the code

The watch callback is executed on the Pre queue by default; The Watch Callback changes itself, making callback join Pre’s queue again.

Now, of course, you don’t usually write it this way that causes recursion directly, recursion is usually indirect. For example, watch modifies a ref, and ref triggers dependence, which changes the value monitored by Watch, leading to recursion.

conclusion

Using queue model, can better describe, better decoupling, vUE life cycle, component update, one-way data flow design, etc.

The component updates the queue using a queueable queue with a Job ID.

  1. Delayed execution removes duplicate jobs to improve performance
  2. This ensures that the component executes the parent component first and the child component updates in a one-way order
  3. In the case of recursion, it can correctly handle the component update order and ensure the correctness of data

Using the Pre/Post queue, :

  1. Delayed execution removes duplicate jobs to improve performance
  2. If a component is resolved, the declaration period must be executed at a specified time (for example, after the component is Mounted).

The next article will parse the source code for the VUE queue.