咱们常常会在页面 resize 的时候作些操做,好比从新渲染一个图表组件,使其自适应当前页面大小, 可是 window.resize
事件触发的很频繁,因此咱们通常都会加 debounce 作个「去抖」操做,代码以下:html
import debounce from 'lodahs/debounce'
export default {
methods: {
resize: debounce(function () {
// do something
}, 100)
},
mounted () {
window.addEventListener('resize', this.resize)
},
beforeDestroy () {
window.removeEventListener('resize', this.resize)
}
}复制代码
然而,上面的代码是有深坑的(在坑中爬了半天 - . -),下面聊聊个人爬坑历程。vue
<template>
<div id="app">
<chart></chart>
<chart></chart>
</div>
</template>
<script>
const Chart = {
template: '<span>chart</span>',
methods: {
resize: _.debounce(function () {
console.log('resize')
}, 1000 /* 时间设长一点,便于后面的观察 */)
},
mounted () {
window.addEventListener('resize', this.resize)
},
beforeDestroy () {
window.removeEventListener('resize', this.resize)
}
}
new Vue({
el: '#app',
components: {
Chart
}
})
</script>复制代码
页面中有两个 Chart 组件,他们会监听 window.resize
事件,而后在控制台输出 "resize"。bash
如今我拖动页面,改变其大小,1s 后(debounce 设的延迟为 1s),控制台会输出几回 "resize" ?app
这还不简单,难道不是每一个 Chart 组件各输出 1 次,共计 2 次?ide
这里提供一个线上 demo,你们能够去把玩一下,实际上每次改变页面大小,控制台只输出了 1 次 "resize",是否是很诡异?函数
methods
提及假设咱们在组件 Chart 中定义了以下方法:ui
{
methods: {
resize () {}
}
}复制代码
那么有一个与本文很重要的点须要弄清楚:全部该 Chart 组件的实例,调用 this.resize()
时,最后都会调用 this.$options.methods.resize()
,在 vue 内部,组件实例化的时候其实就干了下面这个事:this
// 绑定 this
this.resize = this.options.methods.resize.bind(this)复制代码
这种关系以下图:spa
而后咱们来解释下诡异现象的缘由:.net
两个 Chart 实例中的 resize 会调用同一个 debounce 函数,所以当两个组件同时执行 resize 方法的时候,前者被 debounce 掉了,因此咱们只看到输出了 1 次 "resize"。
因为 methods
中定义的方法是共享的,形成 debounce 效果在组件中相互影响,致使 bug,那么只要避免共享,每一个 resize 相互独立就能够了,改进后的代码以下:
<template>
<div id="app">
<chart></chart>
<chart></chart>
</div>
</template>
<script>
const Chart = {
template: '<span>chart</span>',
created () {
// 将 resize 的定义从 methods 中拿到这里来
// 这样就能保证 resize 只归某个实例拥有
this.resize = _.debounce(function () {
console.log('resize')
}, 1000 /* 时间设长一点,便于后面的观察 */)
},
mounted () {
window.addEventListener('resize', this.resize)
},
beforeDestroy () {
window.removeEventListener('resize', this.resize)
}
}
new Vue({
el: '#app',
components: {
Chart
}
})
</script>复制代码
改进后的 demo
细心的小伙伴可能会发现,不对呀,官方的例子 vuejs.org/v2/guide/mi… 就是将 debounce 放到了 methods
中。在官方的例子中,确实没什么问题,由于一个页面不可能同时有两个输入框在输入,同时调用 expensiveOperation
方法。可是,假设把 debounce 的延迟设大一点,而后快速在两个输入框中切换输入(虽然这个场景几乎不存在),就会出现我一开始说的那个诡异现象。