vue-element-admin的v-waves指令失效问题解决(与@click冲突导致水波纹失效)
Bug report(问题描述)当用户给一个元素(如按钮元素)添加v-waves指令后,如果用户再给元素添加一个click事件句柄,而且这个句柄在执行时触发了组件的更新,那么按钮的“波动”效果就会失效。Steps to reproduce(问题复现步骤)我用vue-element-admin项目master分支代码中的 src/views/table/complex-table.vue 页面中的一
Bug report(问题描述)
当用户给一个元素(如按钮元素)添加v-waves指令后,如果用户再给元素添加一个click事件句柄,而且这个句柄在执行时触发了组件的更新,那么按钮的“波动”效果就会失效。
Steps to reproduce(问题复现步骤)
我用 vue-element-admin 项目 master 分支代码中的 src/views/table/complex-table.vue 页面中的一个search按钮作为示例,因为此处就有这个bug。
- 给按钮元素添加
v-waves指令:
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
Search
</el-button>
- 给按钮元素添加一个
click事件句柄handleFilter:
handleFilter() {
this.listQuery.page = 1
this.getList()
},
getList() {
this.listLoading = true // 触发更新
fetchList(this.listQuery).then(response => {
this.list = response.data.items
this.total = response.data.total
// Just to simulate the time of the request
setTimeout(() => {
this.listLoading = false
}, 1.5 * 1000)
})
}
其中 this.listLoading = true 语句触发了页面组件的更新。
- 页面组件的更新触发了
v-waves指令的updatehook:Vue文档对自定义指令的钩子函数的描述如下:
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
文档说当所在组件的 VNode 更新时调用自定义指令的update钩子,而我所遇到的情况有所不同。
this.listLoading = true 语句触发的是页面组件的更新,按理说el-button组件没有资格更新,但事实是v-waves指令的update钩子确实被触发了。
忽略这件事情,我们来看v-waves指令的update钩子的定义:
update(el, binding) {
el.removeEventListener('click', el[context].removeHandle, false)
el.addEventListener('click', handleClick(el, binding), false)
},
如上所示,第一句删除了按钮元素之前添加的click事件监听器,从而导致事件监听器从始至终都没有执行,“波动”效果当然也不会出现了。
Screenshot or Gif(截图或动态图)
其中第一次点击未出现“波动”效果,因为它触发了页面更新;而第二次点击没有触发更新,所以出现了“波动”效果。
Link to minimal reproduction(最小可在线还原demo)
vue-element-admin 项目 master 分支中的 src/views/table/complex-table.vue 页面中的 Search 按钮。
My Suggestion
回顾一下发生bug的整个过程:
- 用户触发按钮的点击事件。
click事件是一个宏任务源,它发布了两个宏任务:@click的handler和v-waves指令的handler; @click的handler先执行,而它触发了页面组件的更新。页面组件的更新是一个微任务源,它发布了一个更新组件的微任务。由于微任务会在事件循环的末尾执行完,所以会先进行组件的更新,然后才执行v-waves指令的click handler;- 页面组件更新时触发了
v-waves指令的update钩子,在update钩子中删除了之前添加的click事件监听器。
所以,我们只要先执行v-waves指令的click handler,再删除click事件监听器就行了。
一个简单的方法是延迟到一个宏任务中删除监听器:
update(el, binding) {
const removeHandler = el[context].removeHandle
setTimeout(_ => {
el.removeEventListener('click', removeHandler, false)
})
el.addEventListener('click', handleClick(el, binding), false)
},
转载自:https://github.com/panjiachen/vue-element-admin/issues/2785
更多推荐




所有评论(0)