携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第33天,点击查看活动详情
在上一章,我们通过新增一个响应式变量和匿名函数的方法,优化了事件的绑定,减少了事件的解绑,从而提高了些许性能。同时,这种方式还给我们带来了一些意想不到的优势,比如这一章,通过那个匿名函数,我们能更简单处理事件的冒泡。
什么时候有冒泡问题呢,我们假设有这样一个vnode,当初次渲染的时候,可以看见a === false,此时div是没有绑定点击事件的,然后你会直接点击到button上,此时改变了a,div点击事件绑定了。此时你会发现,点击button的时候,div的点击事件也触发了,这就是冒泡问题。
const a = ref(false)
const vnode = {
type:'div',
onClick:a ? ()=>alert('div') : '',
children:[
{
type:'button',
onClick:()=>{
a.value = true
alert('button')
}
}
]
}
这个问题比普通的冒泡问题麻烦了一点,在于中间参杂了vue的响应式变量,父元素的事件是动态绑定的。当点击子元素的时候,改变了响应式变量,触发了更新操作,更新时自动重新渲染了,为父元素加上了事件,此时子元素的事件刚刚触发,于是就是发生了冒泡。
因此,我们发现,父元素绑定事件发生在事件冒泡之前,也就是说vue的更新操作发生在事件冒泡之前。
这个问题并不是vue的问题,我们知道,vue内部维护了微任务的队列去执行更新操作,但是事件冒泡是未可知,无法检测到的,我们没有办法暂停冒泡,然后过一会再放开让他继续冒泡。在这个例子中,我们希望,第一次点击的时候只执行子元素的事件,然后给父元素加上事件,等第二次再点击,那么有冒泡也是正确的。
在书中,作者是这样解决的,它给事件加上了2个时间戳:绑定时间和触发时间,屏蔽所有绑定事件晚于触发时间的事件处理函数的执行。
这个判断可以放到我们之前那个匿名函数中去做处理,虽然vue依旧会给他加上事件,但是我们可以判断它的绑定时间从而拦截执行。