petite-vue 组件的 mounted 回调发生在 nextTick() 任务之后

在使用 petite-vue 开发项目时,有一个列表视图,每个列表项是一个自定义组件。

在向列表视图的数组数据添加数据后,希望通过 EventBus 触发一个事件,让新的列表项组件 mounted 回调可以响应这个事件。

当使用 PetiteVue.nextTick() 传入回调函数来触发事件时,发现新的组件并没有响应该事件。通过调试,发现对 mounted 的调用发生在 nextTick() 回调函数之后。嵌套调用两次 PetiteVue.nextTick() 或者使用 setTimeout() 可以解决。

究其原因,是因为 petite-vue 是异步渲染 DOM 的。在当前任务循环的宏任务中修改响应式数据后,petite-vue 响应数据变化,将响应变化的行为,作为一个函数,通过 Promise 添加到当前任务循环的微任务队列尾部。该响应行为可能会修改 DOM,对 DOM 进行更新、移除或新建。对于要新建的 DOM,如果有对应的组件,会先实例化该组件,然后才创建DOM,之后又将回调该组件 mounted 函数的行为作为一个函数添加到当前事件循环的微任务队列尾部。

为什么不直接回调 mounted,让 mounted 回调在 nextTcik() 添加的任务之前执行,而是在 nextTcik() 添加的任务之后才执行呢?
这是考虑到 nextTcik() 添加的任务可能还会修改数据,从而影响 DOM。
在 nextTcik() 添加的任务之后执行,mounted 回调取得的 DOM 将是 vue 对数据变化响应后的修改的最终版本。

需要注意的是,petite-vue 只有 mounted 与 unmounted 两个生命周期回调,没有 created。

PetiteVue.nextTick() 也是通过 Promise 添加函数到当前任务循环的微任务队列尾部。
PetiteVue.nextTick() 在当前任务循环的宏任务中运行,它直接添加了一个微任务;petite-vue 响应数据变化的行为是一个微任务,在宏任务运行完毕后才运行,它添加的回调组件 mounted 的微任务肯定是在宏任务中添加的微任务之后。
这就是触发 EventBus 事件的行为发生在组件监听 EventBus 事件的行为之前的原因。

而在改变响应式数据后,嵌套调用两次 PetiteVue.nextTick(),第一次调用发生在宏任务中,它添加一个微任务,这个微任务执行第二次 PetiteVue.nextTick(),这次又添加一个微任务队列尾部,在新添加的微任务中触发 EventBus 事件。
回调组件 mounted 的微任务先被添加,触发 EventBus 事件的为微务后被添加,组件也就能响应 EventBus 事件了。

至于 setTimeout(),它将任务添加到宏任务队列,该宏任务会在之后的事件循环中执行。而回调组件 mounted 的微任务在当前事件循环执行,组件自然能响应 EventBus 事件。

posted @ 2024-09-05 17:27  钰琪  阅读(21)  评论(0编辑  收藏  举报