javascript - 取消绑定 Vue 指令生命周期中的窗口事件侦听器
问题描述
我刚刚遇到了一个与 Vue 指令中的事件监听相关的问题。我有一个组件,其中包含以下代码:
function setHeaderWrapperHeight() { ... }
function scrollEventHandler() { ... }
export default {
...
directives: {
fox: {
inserted(el, binding, vnode) {
setHeaderWrapperHeight(el);
el.classList.add('header__unfixed');
window.addEventListener(
'scroll',
scrollEventListener.bind(null, el, binding.arg)
);
window.addEventListener(
'resize',
setHeaderWrapperHeight.bind(null, el)
);
},
unbind(el, binding) {
console.log('Unbound');
window.removeEventListener('scroll', scrollEventListener);
window.removeEventListener('resize', setHeaderWrapperHeight);
}
}
}
...
}
每次更改路由器路径时都会重新渲染此组件,我通过将当前路由路径分配给:key
prop 来实现此行为,因此每当路径更改时,它都会重新渲染。但是问题是尽管事件侦听器没有被删除/销毁,导致可怕的性能问题。那么如何删除事件监听器呢?
解决方案
调用bind
函数会创建一个新函数。侦听器没有被删除,因为您传递给removeEventListener
的函数与您传递给的函数不同addEventListener
。
在指令中的钩子之间进行通信并不是特别容易。官方文档建议使用元素的dataset
,尽管在这种情况下看起来很笨拙:
https://vuejs.org/v2/guide/custom-directive.html#Directive-Hook-Arguments
您可以将侦听器直接作为属性存储在元素上,以便它们在unbind
挂钩中可用。
下面的代码采用了稍微不同的方法。它使用一个数组来保存当前绑定到指令的所有元素。window
无论指令使用多少次,监听器只注册一次。如果当前未使用该指令,则删除该侦听器:
let foxElements = []
function onClick () {
console.log('click triggered')
for (const entry of foxElements) {
clickHandler(entry.el, entry.arg)
}
}
function clickHandler (el, arg) {
console.log('clicked', el, arg)
}
new Vue({
el: '#app',
data () {
return {
items: [0]
}
},
directives: {
fox: {
inserted (el, binding) {
console.log('inserted')
if (foxElements.length === 0) {
console.log('adding window listener')
window.addEventListener('click', onClick)
}
foxElements.push({
el,
arg: binding.arg
})
},
unbind (el, binding) {
console.log('unbind')
foxElements = foxElements.filter(element => element.el !== el)
if (foxElements.length === 0) {
console.log('removing window listener')
window.removeEventListener('click', onClick)
}
}
}
}
})
<script src="https://unpkg.com/vue@2.6.11/dist/vue.js"></script>
<div id="app">
<button @click="items.push(Math.floor(Math.random() * 1000))">Add</button>
<hr>
<button
v-for="(item, index) in items"
v-fox:example
@click="items.splice(index, 1)"
>Remove {{ item }}</button>
</div>
但是,所有这些都假设指令甚至是正确的方法。如果您可以在组件级别执行此操作,那么它可能会变得更简单,因为您拥有可用于存储内容的组件实例。请记住,调用bind
会创建一个新函数,因此您需要在某处保留对该函数的引用,以便将其传递给removeEventListener
.
推荐阅读
- struct - 在HashSet中插入struct时的借用问题
- php - 教义迁移令
- swift - 为什么我必须将 swift 协议中的协议和包含的可选功能都标记为@objc?
- python - 远程使用 Pywinrm 的 Python 3.6
- c++ - 如何使用 nlhoman json 在 C++ 中将相同的关键 json 数据合并为一个
- javascript - 获取在 LUIS 上添加的句子
- android - 使用 recyclerview 滚动 coordinatorLayout 中的 Android 抖动错误(instagram 也有此错误)
- c++ - 何时在 C++ 中的嵌套类上使用 Pimpl 模式,反之亦然?
- javascript - 如何在异常捕获中获取数据库引用?
- botframework - Microsoft 团队免费帐户:此卡操作已禁用,因为 BOT 已被您的管理员阻止