咱們常常會在頁面 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 的延遲設大一點,而後快速在兩個輸入框中切換輸入(雖然這個場景幾乎不存在),就會出現我一開始說的那個詭異現象。