Vue集中了滚动和调整大小事件(避免在组件中重复)

时间:2019-01-15 16:17:09

标签: javascript vue.js vue-component

我有这样的Vue结构:

App
 |--{ data: { scrollInfo {...} } }
 |
 |--Component1
 |      |--{ data: { sidebarWidth: 500 }
 |
 |--Component2
 |      |--{ data: { overlayItemWidth: 389 }
 |
 |--Component3
 ...
 ...

在我的每个组件中,我都有一些元素,这些元素的变量应在滚动和调整大小时更改(例如sidebarWidthoverlayItemWidth)。我发现这个post here展示了这种为滚动和调整大小添加侦听器的方式。

我已将其添加到主实例中,如下所示:

data: {
  scrollInfo: {
    scrollFromTop: 0,
    viewportHeight: 0,
    viewportWidth: 0,
    pageHeight: 0
  }
},
created() {
  window.addEventListener( 'scroll', this.calculateScroll );
  window.addEventListener( 'resize', this.calculateViewport );
  window.addEventListener( 'resize', this.calculatePageScrollSpecs );
  this.calculateViewport();
  this.calculateScroll();
  this.calculatePageScrollSpecs();
},
destroyed() {
  window.removeEventListener( 'scroll', this.calculateScroll );
  window.removeEventListener( 'resize', this.calculateViewport );
  window.removeEventListener( 'resize', this.calculatePageScrollSpecs );
}

我不会显示方法的内容(calculateScrollcalculateViewport,...),因为它与该问题无关。

现在...在我的组件中,我有一些变量应该在滚动和调整大小时进行更改和重新评估。但是,每当我有一个这样的变量时,当前我都会在createddestroyed中添加这些相同的侦听器,然后将相同的事件侦听器添加到给定的组件中,然后根据新的变量进行计算。方法在那里。看起来又长又笨拙。

有没有办法让我在每个组件中都拥有这些window.AddEventListener,而在我的根实例中只有那些?

...我在想,如果我的主要实例中有一系列“需要在滚动或调整大小时重新计算的东西”,但我不确定是否这样会很混乱,因为组件的变量实际上并不会保留在组件中,而是用this.$root.sidebarWidth进行引用。这也将使我的主要实例变得庞大。

有什么建议吗?

3 个答案:

答案 0 :(得分:1)

很高兴为您找到了一个解决方案,但对于可能想要更舒适/更通用的解决方案的未来访客:几个月前,我遇到了同样的问题,并在npm上放置了一个Vue mixin,它确实可以做到这一点-一次处理全局数据-终身,而不是每个安装的组件一次。

它称为Reactivator,这是您如何使用它为window.resize创建全局处理程序的方法:

// viewport-width.js

export default {
  // Reactivator uses `getInitialState` to fetch the
  // initial value before any listener was attached.
  getInitialState() {
    return window.innerWidth
  },

  // Reactivator calls `listen` when the first component using
  // it is created and executes the returned cleanup callback
  // when it's the last component using it is destroyed.
  listen(set) {
    const resizeHandler = () => set(window.innerWidth)

    window.addEventListener('resize', resizeHandler)

    return () => window.removeEventListener('resize', resizeHandler)
  }
}


// component.js

import reactivator from 'vue-reactivator'
import viewportWidth from './viewport-width.js'

export default {
  mixins: [
    // `viewportWidth` will now be a reactive property in this component!
    reactivator({ viewportWidth })
  ]
}

也就是说,观察视口大小是一种常见的情况,我将其(以及其他有用的东西,例如滚动位置或鼠标位置)放入了额外的常见Reactivator实现包中。您可以在vue-browser-state的npm上找到它。

答案 1 :(得分:0)

您应该制作一个自定义组件,该组件可以接受更改内容并执行所有需要的操作(例如添加和删除事件侦听器)作为道具。

答案 2 :(得分:0)

好吧...根本没有答案,所以我自己潜入其中。这是我能想到的最好的。我希望它可以帮助有需要的其他人。

我在使用extendssource)或mixinssource)之间感到痛苦。

经过一番研究,我最终使用了mixins(this video达到了我想要达到的目标以及如何解决它的目的)。如果您想使Mixin成为全局组件,请在4:11左右开始观看此视频。我不是在下面写的说明中这样做。

请注意,我将webpacklaravel-mix结合使用,并编写了以下解决方案。它用于WordPress安装中。

披露

这是不计其数的不同答案和文章的混合。这将永远归功于正确的所有者和来源,所以我什至不愿意尝试。抱歉。

mixins文件(./mixins/scrollAndResize.js)。

export const scrollAndResizeMixin = {
  created() {
    console.log( 'scrollAndResizes loaded' );
    this.calcScroll();
    this.calcPageAndViewport();
  },
  data: function() {
    return {
      scrollFromTop: 0,
      viewportHeight: 0,
      viewportWidth: 0,
      pageHeight: 0
    }
  },
  methods: {
    calcScroll: function (){
      let doc = document.documentElement;
      this.scrollFromTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);

      // DEBUG (uncomment and scroll, to check if it works)
      // console.log( (window.pageYOffset || doc.scrollTop) );
      // console.log( (doc.clientTop || 0) );
    },
    calcPageAndViewport: function(){
      // Viewport info
      this.viewportHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
      this.viewportWidth = Math.min(document.documentElement.clientWidth, window.innerWidth || 0);

      // Page length
      var body = document.body;
      var html = document.documentElement;
      this.pageHeight = Math.max(
        body.scrollHeight,
        body.offsetHeight,
        html.clientHeight,
        html.scrollHeight,
        html.offsetHeight
      );
    },

  }
};

主js文件(./app.js)。

// Vue
import Vue from 'vue';

// Mixins
import { scrollAndResizeMixin } from './mixins/scrollAndResize';

// The Application
const app = new Vue({
  mixins: [
    scrollAndResizeMixin
  ],
  computed: {
    mobile: function() {
      return this.viewportWidth < 992; // This uses the mixin
    }
  },
  created() {
    window.addEventListener( 'scroll', this.calcScroll );
    window.addEventListener( 'resize', this.calcPageAndViewport );
  },
  destroyed() {
    window.removeEventListener( 'scroll', this.calcScroll );
    window.removeEventListener( 'resize', this.calcPageAndViewport );
  }
}); 

和/或仅在类似这样的组件中使用它...

<template>

  <div>
    <p v-if="viewportWidth > 992">
      Desktop
    </p>
    <p v-else>
      Mobile
    </p>   
  </div>

</template>



<script>
  import { scrollAndResizeMixin } from '../mixins/scrollAndResize';

  export default {
    mounted() {
    },
    mixins: [
      scrollAndResizeMixin
    ],
    created() {
      window.addEventListener('scroll', this.calcScroll);
      window.addEventListener('resize', this.calcPageAndViewport);
    },
    destroyed() {
      window.removeEventListener('scroll', this.calcScroll);
      window.removeEventListener('resize', this.calcPageAndViewport);
    }
  }
</script>