vue3异步更新原理

583 阅读2分钟

Vue的异步更新机制

我们都知道Vue的更新是异步,那我们就知道在js中,异步任务就依赖于事件循环的机制。事件循环的机制就是一个执行模型,用于处理异步任务的执行顺序。那Vue就是借助了这一点,就实现了异步更新。

举个简单的例子

<script setup lang="ts">
import { ref } from "vue";
const state = ref({ count: 0 });

const fn = () => {
  state.value.count = 2;
  state.value.count = 3;
  state.value.count = 4;
  state.value.count = 5;
  state.value.count = 6;
  console.log(state.value.count);
};
</script>

<template>
  <div @click="fn">{{ state.count }}</div>
</template>

当我们在执行上面方法的时候,多次修改了数据,如果我们每一次修改数据,就要进行页面渲染,那我们的效率就很低,我们会把多次修改的数据监听到的方法放在一个队列中,最后统一处理后,只进行一次页面的渲染,页面指挥渲染最后一次 state.count 6

Vue的异步实现过程

我们通过上面知道了Vue的异步更新原理,那我们该如何实现以上功能呢?

1.我们想到既然是要异步执行,那么我们的Promise肯定是要用上的

2.我们在多次改变值的过程中,多次调用trigger执行effect时,我们就得把这些变更收集起来

所以我们很容易就写出来以下代码:

queueJob

  • 该方法负责维护一个主任务队列,接受函数作为参数,会将参数push到queue队列中。在当前宏任务执行完成后,清空队列
let queue = [];
export function queueJob(job) {
    if(!queue.includes(job)){
        queue.push(job);
        queueFlush();
    }
}

queueFlush

  • 该方法负责尝试创建微任务,等待任务队列执行
let isFlushPending = false;
function queueFlush(){
    if(!isFlushPending){
        isFlushPending = true;
        Promise.resolve().then(flushJobs)
    }
}

flushJobs

  • 根据Id排队队列,这就是先刷新父在刷新子的原因
  • 遍历执行队列的任务
  • 执行完毕后在清空队列
function flushJobs(){
    isFlushPending = false
    // 清空时  我们需要根据调用的顺序依次刷新  
    // 保证先刷新父在刷新子
    queue.sort((a,b) => a.id - b.id);
    for(let i = 0; i < queue.length; i++){
        const job = queue[i];
        job();
    }
    queue.length = 0;
}

总结

Vue 的异步更新机制带来了以下几个优势:

  • 性能优化:将多个数据变更合并成一次更新,避免频繁的 DOM 操作,提高渲染效率。
  • 批量更新:将数据变更放入一个队列中,只在下一个事件循环中进行更新,避免同步更新可能引发的性能问题和 UI 闪烁现象