为什么 响应式数据被重新赋值了三次,但是监听回调只执行了一次

const { createApp } = Vue
const app = createApp({
    components: [],
    template: `
    <div>
        <button @click="handleClick">toggle</button>
    </div>
    `,
    data() {
        return {
            visible: false,
            test: 1,
        }
    },
    methods: {
        handleClick() {
            this.visible = true;
            this.visible = false;
            this.visible = true;
        },
    },
    watch: {
        visible(v) {
            console.log('visible change', v)
        },
    }
}).mount('#app')

上述代码点击toggle后的效果如下图。visible change只log了一次。
在这里插入图片描述

为什么 响应式数据被多次修改,但是监听回调只执行了一次呢?
Vue 通过实例化 ReactiveEffect ,监听getter的改变,回调scheduler。通过这种方式去劫持 visible 的更改。

// packages\runtime-core\src\apiWatch.ts
let scheduler: EffectScheduler
if (flush === 'sync') {
    scheduler = job as any
} else if (flush === 'post') {
    scheduler = () => queuePostRenderEffect(job, instance && instance.suspense)
} else {
    job.pre = true
    if (instance) job.id = instance.uid
    scheduler = () => queueJob(job)
}

const effect = new ReactiveEffect(getter, scheduler) 
// getter在本场景中对应visible。scheduler会在getter更改时被调用

代码 1 执行后, visible 设置为 true 。Vue 监听到 visible 的更改。执行 queueJob ,将负责执行回调函数的 job 推入 queue 。并利用 Promise.then 的回调函数会被放入微任务队列的特性,使用 then 将执行 queue 中所有方法的任务放入微任务队列中。

// packages\runtime-core\src\scheduler.ts
export function queueJob(job: SchedulerJob) {
  // 检查queue是否为空、job是否存在queue中
  if (
    !queue.length ||
    !queue.includes(
      job,
      isFlushing && job.allowRecurse ? flushIndex + 1 : flushIndex
    )
  ) {
    if (job.id == null) {
      queue.push(job)
    } else {
      queue.splice(findInsertionIndex(job.id), 0, job)
    }
    queueFlush()
  }
}
function queueFlush() {
  if (!isFlushing && !isFlushPending) {
    isFlushPending = true
    currentFlushPromise = resolvedPromise.then(flushJobs) // flushJobs进入微任务
  }
}

代码 2 执行后, visible 设置为 false。Vue 知道 visible 更改后,发现 负责 visible 更新时回调的 job 已经推入 queue,于是不再继续执行推入操作。
代码 3 执行后,visible 设置为 true。后面同上,不再继续执行推入操作。
handleClick 执行完毕。事件循环机制会去执行微任务队列的任务。然后代码1执行后放入微任务队列中的 job 得到了执行,回调函数也得到了执行。
综上所述,job因为只推入1次,所以只执行了一次,监听回调也只执行了一次。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值