Vue设置了IntersectionObserver

时间:2017-12-21 10:00:01

标签: javascript vue.js vuejs2 vue-component intersection-observer

我正在尝试为无限滚动列表设置IntersectionObserver。我想检查IntersectionObserver是否已达到列表的最后一项。

到目前为止,我的 IntersectionObserver 设置如下所示:

mounted() {
    const config = {
        treshold: 1
    };

    const observer = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
            if (entry.intersectionRatio > 0) console.log(entry.target);
        });
    }, config);

    observer.observe(this.lastRenderedDeal());
}

我的列表项的呈现方式如下:

<deal :deal="deal"
      :key="index"
      ref="deals"
      v-for="(deal, index) in deals"
</deal>

要获取最后一个 renderedDeal ,我在交易中使用ref

lastRenderedDeal() {
    const deals = this.$refs.deals;
    return deals ? deals[deals.length - 1].$el : null;
}

这适用于初始设置并触发。问题是,当我想要无限滚动时,我需要继续追加交易列表。所以我的lastRenderedDeal会不断更新,以反映附加到我的列表中的最后一笔交易。

这种反应性似乎没有传递给observer.observe方法。它只能拿起我的初始元素。这似乎很明显,因为它已在mounted钩子中实例化,但我该如何处理呢?

我是否需要为交易设置观察者并再次呼叫观察者。如果是这样,我可以简单地告诉它替换最初观察到的项目吗?

1 个答案:

答案 0 :(得分:3)

您可以观察一个始终位于列表底部的虚拟<div>。您可能需要在key上设置<div>以确保Vue在每次渲染组件时都不会重新创建元素。

我总是希望制作一个包含此行为的通用且可重用的组件。这是我的看法:

const InfiniteScroll = {
  render(h) {
    return h('div', [
      ...this.$slots.default,

      // A dummy div at the bottom of the list which we will observe.
      // We must set a key on this element so that Vue reuses
      // the same element it initially created upon each rerender.
      h('div', {
        key: 'footer',
        ref: 'footer',
        style: {
          height: '1px'
        },
      }),
    ]);
  },

  mounted() {
    this.observer = new IntersectionObserver(entries => {
      // We only have one entry. Is it visible?
      if (entries[0].intersectionRatio > 0) {
        this.$emit('trigger');
      }
    });

    // Observe the dummy footer element
    this.observer.observe(this.$refs.footer);
  },
};

new Vue({
  el: '#app',
  components: {
    InfiniteScroll,
  },
  data: {
    items: [],
  },
  created() {
    this.loadMore();
  },
  methods: {
    loadMore() {
      for (let i = 0; i < 20; i++) {
        this.items.push(this.items.length + 1);
      }
    },
  },
});
body {
  margin: 0;
}

.item {
  border-bottom: 1px solid #eee;
  padding: 10px;
}
<script src="https://rawgit.com/vuejs/vue/dev/dist/vue.js"></script>

<div id="app">
  <infinite-scroll @trigger="loadMore">
    <div v-for="item of items" class="item">{{ item }}</div>
  </infinite-scroll>
</div>