vue 踩坑小记 - 如何正确的使用 debounce

咱们常常会在页面 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"。

将 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 的延迟设大一点,而后快速在两个输入框中切换输入(虽然这个场景几乎不存在),就会出现我一开始说的那个诡异现象。

相关文章
相关标签/搜索