watch中还有onCleanup这种东西?!
有的兄弟,有的。

你熟悉的watch其实长这样。
我们看看官方对它的解释:

看完之后是不是感觉似懂非懂的?
一句话概括,其实onCleanup解决的是,watch中的副作用函数多次执行时,任务顺序的问题。
我们来看一个开发案例:
const r1 = ref('haluo') //假设这个是一个输入框的绑定值
// 接下来我们模拟一个请求
let timer=3000;
function getData(timer) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(timer);
}, timer);
});
}
// 我们监听值的变化,变一次发送一次请求
watch(
r1,
async (newValue, oldValue, onCleanup) => {
timer -= 1000;
let r = await getData(timer);
console.log('-->', r);
},
{
flush: "sync",
}
);
// 应该很好理解,每执行一次请求,timer减少1s
// 然后假设我们触发了两次值的变化
setTimeout(() => {
r1.value = '1'
r1.value = '2'
}, 1000);
timer初始值是3000,每次减少1000。
按照我们正常的理解,这两次执行的结果是不是应该是:2000 >>> 1000
但实际呢?

结果是 1000 >>> 2000
这怎么能行呢,我们要的肯定是最新一次的结果,也就是1000噻。
所以这时候onCleanup就产生作用了。
// 现在我们加入onCleanup
watch(
r1,
async (newValue, oldValue, onCleanup) => {
// 我们初始化一个值flag
let flag = true;
timer -= 1000;
// 在onCleanup中去改变它的值,记得onCleanup要在await前面
onCleanup(() => {
flag = false;
})
let r = await getData(timer);
// 根据flag的值取判断是否渲染
if (flag) {
console.log('-->', r);
}
},
{
flush: "sync",
}
);
这时候再看控制台的值,就是我们想要的结果了。
有点神奇是吧,是怎么实现的呢?
其实说白了,就是一个闭包,接下来我们不用onCleanup,换个方法改造一下watch的回调函数。
let queue = [];
watch(
r1,
async (newValue, oldValue) => {
let flag = true;
timer -= 1000;
// 现在我们不用onCleanup 而是在外面新建了一个数组queue
// 把queue中的函数取出来挨个执行一遍
for (let i = 0; i < queue.length; i++) {
queue[i]();
}
// 每次执行,我们为queue推入一个函数,把flag变成false
queue.push(() => (flag = false));
let r = await getData(timer);
if (flag) {
console.log('-->', r);
}
},
{
flush: "sync",
}
);
看到没有,效果一样

不太理解?我们推导一下执行过程。

核心就是,每次函数执行,都会开辟新的执行环境,这里面的flag都是独立的,同时又保留了上一个flag的清理方法,将它变成false。
这就是onCleanup的实现原理,其实理解之后就非常简单了。

源码里面其实就是把你传到onCleanup的fn存起来了,每次执行的时候就把上一次的fn执行一下。
至于vue3.5的 onWatcherCleanup Api,想必聪明的你也知道怎么回事了吧。
学废了吗?


673

被折叠的 条评论
为什么被折叠?



