We often do something with page resize, such as re-rendering a chart component to fit the current page size, but window.resize is triggered so frequently that we usually do a “debounce” operation as follows:

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)
  }
}Copy the code

However, the code above is a pit (climb in the pit for a long time -.. -), let’s talk about my climb in the pit.

Let’s look at an example

<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 /* time set a bit longer for later observation */)},mounted () {
    window.addEventListener('resize', this.resize)
  },

  beforeDestroy () {
    window.removeEventListener('resize', this.resize)
  }
}

new Vue({
  el: '#app',
  components: {
    Chart
  }
})
</script>Copy the code

There are two Chart components on the page that listen for the window.resize event and print “resize” on the console.

Now I drag the page, change its size, and after 1s (debounce is set to 1s), how many times does the console output “resize”?

This is not easy, is not each Chart component output once, a total of two times?

Here’s an online demo that you can play with, but every time YOU change the page size, the console only prints “resize” once. Isn’t that weird?

frommethodsSpeaking of

Suppose we define the following methods in the component Chart:

{
  methods: {
    resize() {}}}Copy the code

This.$options.methods.resize() will be called at the end of all Chart component instances. Inside the vue, the component instantiation actually does the following:

/ / bind this enclosing the resize = this. The options. The methods. The resize. Bind (this)Copy the code

This relationship is shown below:

And here’s why:

Resize in both Chart instances calls the same debounce function, so when both components execute resize at the same time, the former is debounce, so we only see “resize” printed once.

Separate the resize method

Since the methods defined in Methods are shared, causing the Debounce effect to interact with each other in the component, resulting in bugs, it is only necessary to avoid sharing and make each resize independent of each other. The improved code is as follows:

<template>
  <div id="app">
    <chart></chart>
    <chart></chart>
  </div>
</template>

<script>
const Chart = {
  template: '<span>chart</span>'.createdThis.resize = _. Debounce () {// return the definition of resize from methods // so that resize is only owned by an instance.function () {
      console.log('resize'}, 1000 /* time set a bit longer for later observation */)},mounted () {
    window.addEventListener('resize', this.resize)
  },

  beforeDestroy () {
    window.removeEventListener('resize', this.resize)
  }
}

new Vue({
  el: '#app',
  components: {
    Chart
  }
})
</script>Copy the code

Improved demo

One last word

Careful partners may find that the official example vuejs.org/v2/guide/mi… You put Debounce into methods. In the official example, there’s really nothing wrong because you can’t have two input fields on one page and call the expensiveOperation method at the same time. However, if you set debounce to a larger delay and then quickly switch between the two input boxes (although this scenario is almost non-existent), the weird phenomenon I described at the beginning will occur.