Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

长列表优化(Vue) #31

Open
cookiepool opened this issue Jan 8, 2020 · 0 comments
Open

长列表优化(Vue) #31

cookiepool opened this issue Jan 8, 2020 · 0 comments

Comments

@cookiepool
Copy link
Owner

cookiepool commented Jan 8, 2020

使用第三方插件

  • vue-virtual-scroller
  • vue-virtual-scroll-list

自己实现

解决的问题

  • 限制列表对用户可见的元素数量。我们把可见区域称为 ViewPort
  • 当列表滚动时,列表中的其他元素怎么由不可见变为可见?
  • 监听列表容器元素的滚动事件,当列表里的元素进入可视区域,则添加到DOM中
  • 问题是如果一直这么滚下去,列表会越来越大。所以需要在列表元素离开 ViewPort 的时候从DOM中移除
  • 问题又来了,由于 ViewPort 刚好是一屏的大小,滚动的时候元素还没来得及渲染,会出现一段时间的空白。解决办法就是上下增加一部分数据渲染。

思路

  • 首先容器的高度、列表内容的高度一定。
  • 找到你要显示的数据的开始索引和结束索引,从总的数据列表里面截取。
  • 计算偏移量,使用transform属性进行移动,否则看不到数据。
  • 列表和占位使用绝对定位,脱离文档流,减少重绘和重排。

先贴一个简单的入门代码:

<template>
  <div class="all-wraper">

    <!-- 容器,这儿我以整个视窗的大小为容器的大小 -->
    <!-- 并且列表里面子项、容器这些的高度一定 -->
    <div ref="list" class="virtual-list-container" 
      @scroll="handleScroll"
      :style="{height: screenHeight + 'px'}"
    >
      <!-- 这个用来占位,让页面形成滚动条,这个占位的高度为所有内容渲染完时容器本应有的高度 -->
      <div :style="{'height': listHeight + 'px'}" class="virtual-list-placehold"></div>

      <!-- 内容的渲染区域 -->
      <div :style="{'transform': getTranslate3d}" class="virtual-list-list">
        <p class="virtual-list-item"
          v-for="item in visibleData"
          :key="item._id"
          :style="{'height': itemHeight + 'px', 'line-height': itemHeight + 'px'}"
        >
        {{ item.name }}
        </p>
      </div>
    </div>
  </div>
</template>

<script>
// 导入数据5000条
import data from '../assets/data.json';

export default {
  data() {
    return {
      itemData: data, // 要渲染的数据
      itemHeight: 40, // 渲染的列表里面的单个项目的高度

      screenHeight: 0, // 可视区域的高度
      startIndex: 0, // 起始索引
      endIndex: 0, // 结束索引
      offSetValue: 0, // 偏移量,如果不偏移列表,你会发现你往下面滚动的时候看不到内容了
    }
  },
  computed: {
    // 列表的总高度
    listHeight() {
      return this.itemData.length * this.itemHeight;
    },
    // 可视区能显示的列表项目的数量
    visibleCount() {
      return Math.ceil(this.screenHeight / this.itemHeight);
    },
    // 获取可视区域要显示的数据
    visibleData() {
      // Math.min那个地方这么写是判断滚动到底部时候
      return this.itemData.slice(this.startIndex, Math.min(this.endIndex, this.itemData.length));
    },
    // 计算偏移量的样式
    getTranslate3d() {
      return `translate3d(0, ${this.offSetValue}px, 0)`;
    }
  },
  mounted() {
    this.screenHeight = window.innerHeight;
    this.startIndex = 0;
    this.endIndex = this.startIndex + this.visibleCount;
  },
  methods: {
    handleScroll() {
      // 当前列表滚动的位置
      let scrollTop = this.$refs.list.scrollTop;
      // 这时起始的索引
      this.startIndex = Math.floor(scrollTop / this.itemHeight);
      // 这时结束的索引
      this.endIndex = this.startIndex + this.visibleCount;
      // 偏移量
      this.offSetValue = scrollTop - (scrollTop % this.itemHeight);
    }
  }
}
</script>

<style scoped>
  .virtual-list-container {
    overflow: auto;
    position: relative;
    -webkit-overflow-scrolling: touch;
  }
  .virtual-list-placehold {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    z-index: -1;
  }
  .virtual-list-list {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    text-align: center;
  }
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant