最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

不同場景下Vue中虛擬列表實現(xiàn)

 更新時間:2023年10月31日 15:32:04   作者:通往自由之路  
虛擬列表用來解決大數(shù)據(jù)量數(shù)據(jù)渲染問題,由于一次性渲染性能低,所以誕生了虛擬列表渲染,下面我們就來學(xué)習(xí)一下不同場景下Vue中虛擬列表是如何實現(xiàn)的吧

虛擬列表用來解決大數(shù)據(jù)量數(shù)據(jù)渲染問題,由于一次性渲染性能低,所以誕生了虛擬列表渲染。該場景下可視區(qū)高度都是相對固定的。相對固定是指在一定條件下可以被改變。

虛擬列表的要做的事是確保性能的前提下,利用一定的技術(shù)模擬全數(shù)據(jù)一次性渲染后效果。

主要通過兩件事:

1,渲染數(shù)據(jù),適時變更渲染數(shù)據(jù)

2,模擬滾動效果

場景一、可視區(qū)高度固定,單條數(shù)據(jù)高度固定且相同

比如element-ui el-select下拉選擇框,這是最簡單的場景。相關(guān)參數(shù)固定,也最好實現(xiàn)。

<template>
  <div ref="list" class="render-list-container" @scroll="scrollEvent($event)">
    <div class="render-list-phantom" :style="{ height: listHeight + 'px' }"></div>
    <div class="render-list" :style="{ transform: getTransform }">
      <template
        v-for="item in visibleData"
      >
        <slot :value="item.value" :height="item.height + 'px'"  :index="item.id"></slot>
      </template>
    </div>
  </div>
</template>

<script>
export default {
  name: 'VirtualList',
  props: {
    // 所有列表數(shù)據(jù)
    listData: {
      type: Array,
      default: () => []
    },
    // 每項高度
    itemSize: {
      type: Number,
      default: 50
    }
  },
  computed: {
    // 列表總高度
    listHeight () {
      return this.listData.length * this.itemSize
    },
    // 可顯示的列表項數(shù)
    visibleCount () {
      return Math.ceil(this.screenHeight / this.itemSize)
    },
    // 偏移量對應(yīng)的style
    getTransform () {
      return `translate3d(0,${this.startOffset}px,0)`
    },
    // 獲取真實顯示列表數(shù)據(jù)
    visibleData () {
      return this.listData.slice(this.start, Math.min(this.end, this.listData.length))
    }
  },
  mounted () {
    this.screenHeight = this.$el.clientHeight
    this.start = 0
    this.end = this.start + this.visibleCount
  },
  data () {
    return {
      // 可視區(qū)域高度
      screenHeight: 0,
      // 偏移量
      startOffset: 0,
      // 起始索引
      start: 0,
      // 結(jié)束索引
      end: null
    }
  },
  methods: {
    scrollEvent () {
      // 當(dāng)前滾動位置
      const scrollTop = this.$refs.list.scrollTop
      // 此時的開始索引
      this.start = Math.floor(scrollTop / this.itemSize)
      // 此時的結(jié)束索引
      this.end = this.start + this.visibleCount
      // 此時的偏移量
      this.startOffset = scrollTop - (scrollTop % this.itemSize)
    }
  }
}
</script>

<style scoped>
.render-list-container {
  overflow: auto;
  position: relative;
  -webkit-overflow-scrolling: touch;
  height: 200px;
}

.render-list-phantom {
  position: absolute;
  left: 0;
  right: 0;
  z-index: -1;
}

.render-list {
  text-align: center;
}

</style>

初始化渲染數(shù)據(jù)

單條數(shù)據(jù)高度確定,可視區(qū)高度確定??梢杂枚哂嬎愠鲲@示條數(shù)。初始索引為0,結(jié)束索引為顯示條數(shù)。之后截取總數(shù)據(jù)當(dāng)中對應(yīng)的數(shù)據(jù)即可。

 // 可顯示的列表項數(shù)
    visibleCount () {
      return Math.ceil(this.screenHeight / this.itemSize)
    },
...
  mounted () {
    // 開始索引
    this.start = 0
    // 結(jié)束索引
    this.end = this.start + this.visibleCount
  },

滾動邏輯

可視區(qū)高度確定,列表需要模擬出滾動條,這里采用占位div撐開方案。隨著滾動條滾動,監(jiān)聽滾動高度計算出開始索引和結(jié)束索引,再計算出滾動偏移量,再利用translate3d滾動,translate3d且因為沒有重排重繪,所以性能更好。

  methods: {
    scrollEvent () {
      // 當(dāng)前滾動位置
      const scrollTop = this.$refs.list.scrollTop
      // 此時的開始索引
      this.start = Math.floor(scrollTop / this.itemSize)
      // 此時的結(jié)束索引
      this.end = this.start + this.visibleCount
      // 此時的偏移量 
      this.startOffset = scrollTop - (scrollTop % this.itemSize)
    }
  }

再渲染邏輯

隨著滾動條滾動,監(jiān)聽滾動高度計算出開始索引和結(jié)束索引,重置開始和結(jié)束索引,也就自然引發(fā)Vue重新渲染。

場景二、可視區(qū)高度固定,單條數(shù)據(jù)高度確定但不相同

如果單條數(shù)據(jù)不固定,一定是因為有不同的數(shù)據(jù)展示方式,每種方式可以封裝成組件,之后動態(tài)展示。這塊封裝了三個組件,高度分別為20px、30px、50px。

封裝組件

<template>
    <div style="height:20px;border:1px solid #333;">
        height:20px;---{{ index }}
    </div>
</template>
<script>
export default {
  props: ['index']
}
</script>
<template>
    <div style="height:30px;border:1px solid #333;">
        height: 30px---{{ index }}
    </div>
</template>
<script>
export default {
  props: ['index']
}
</script>
<template>
    <div style="height:50px;border:1px solid #333;">
        height: 50px--{{ index }}
    </div>
</template>
<script>
export default {
  props: ['index']
}
</script>

整體實現(xiàn)

<template>
  <div ref="list" class="render-list-container" @scroll="scrollEvent($event)">
    <div
      class="render-list-phantom"
      :style="{ height: listHeight + 'px' }"
    ></div>
    <div class="render-list" :style="{ transform: getTransform }">
      <template v-for="item in visibleData">
        <slot :type="item.type" :index="item.id"></slot>
      </template>
    </div>
  </div>
</template>

<script>
export default {
  name: 'VirtualList',
  props: {
    // 所有列表數(shù)據(jù)
    listData: {
      type: Array,
      default: () => []
    }
  },
  computed: {
    // 列表總高度
    listHeight () {
      return this.listData.reduce((acc, curVal) => {
        return acc + curVal.height
      }, 0)
    },
    // 可顯示的列表項數(shù)
    visibleCount () {
      let accHeight = 0
      let count = 0
      for (let i = 0; i < this.listData.length; i++) {
        accHeight += this.listData[i].height
        if (accHeight >= this.screenHeight) {
          count++
          break
        }
        count++
      }
      return count
    },
    // 偏移量對應(yīng)的style
    getTransform () {
      return `translate3d(0,${this.startOffset}px,0)`
    },
    // 獲取真實顯示列表數(shù)據(jù)
    visibleData () {
      return this.listData.slice(
        this.start,
        Math.min(this.end, this.listData.length)
      )
    }
  },
  mounted () {
    this.screenHeight = this.$el.clientHeight
    this.end = this.start + this.visibleCount
  },
  data () {
    return {
      // 可視區(qū)域高度
      screenHeight: 0,
      // 偏移量
      startOffset: 0,
      // 起始索引
      start: 0,
      // 結(jié)束索引
      end: null
    }
  },
  methods: {
    getStart (scrollTop) {
      var height = 0
      var start = 0
      var i = 0
      while (true) {
        const currentItem = this.listData[i].height
        if (currentItem) {
          height += currentItem
          if (height >= scrollTop) {
            start = i
            break
          }
        } else {
          break
        }
        i++
      }

      return start
    },
    scrollEvent () {
      // 當(dāng)前滾動位置
      const scrollTop = this.$refs.list.scrollTop
      // 此時的開始索引
      this.start = this.getStart(scrollTop)
      // 此時的結(jié)束索引
      this.end = this.start + this.visibleCount
      const offsetHeight = scrollTop - (this.visibleData.reduce((acc, curVal) => acc + curVal.height, 0) - this.screenHeight)
      // 此時的偏移量
      this.startOffset = offsetHeight < 0 ? 0 : offsetHeight
    }
  }
}
</script>

  <style scoped>
.render-list-container {
  overflow: auto;
  position: relative;
  -webkit-overflow-scrolling: touch;
  height: 200px;
}

.render-list-phantom {
  position: absolute;
  left: 0;
  right: 0;
  z-index: -1;
}

.render-list {
  text-align: center;
}
</style>

初始化渲染邏輯

通過累加組件高度的方式算出初始化顯示條數(shù),初始化開始索引為0,結(jié)束索引為顯示條數(shù)

 // 可顯示的列表項數(shù)
    visibleCount () {
      let accHeight = 0
      let count = 0
      for (let i = 0; i < this.listData.length; i++) {
        accHeight += this.listData[i].height
        if (accHeight >= this.screenHeight) {
          count++
          break
        }
        count++
      }
      return count
    },

滾動邏輯

依然采用占位div撐開方案保證可以滾動。隨著滾動條滾動,監(jiān)聽滾動高度,再通過累加方式算出最新的開始索引和結(jié)束索引,算出滾動偏移量

  methods: {
    getStart (scrollTop) {
      var height = 0
      var i = 0
      while (true) {
        const currentItem = this.listData[i].height
        height += currentItem
        if (height >= scrollTop) {
            start = ++i
            break
        }
        i++
      }

      return i
    },
    scrollEvent () {
      // 當(dāng)前滾動位置
      const scrollTop = this.$refs.list.scrollTop
      // 此時的開始索引
      this.start = this.getStart(scrollTop)
      // 此時的結(jié)束索引
      this.end = this.start + this.visibleCount
      const offsetHeight = scrollTop - (this.visibleData.reduce((acc, curVal) => acc + curVal.height, 0) - this.screenHeight)
      // 此時的偏移量
      this.startOffset = offsetHeight < 0 ? 0 : offsetHeight
    }
  }

再渲染邏輯

監(jiān)聽滾動高度計算出開始索引和結(jié)束索引,重置開始和結(jié)束索引,也就自然引發(fā)Vue重新渲染。

測試

<template>
<div class="render-show">
  <div>
    <VirtualList :listData="data">
       <template slot-scope="{type, index}">
          <component :is="type" :index="index"></component>
       </template>
    </VirtualList>
  </div>
</div>
</template>

<script>
import VirtualList from './parts/VirtualList'

import Height20 from './parts/Height20'
import Height30 from './parts/Height30'
import Height50 from './parts/Height50'

const d = []
for (let i = 0; i < 1000; i++) {
  const type = i % 3 === 0 ? i % 2 === 0 ? 'Height30' : 'Height50' : 'Height20'
  d.push({ id: i, value: i, type: type, height: type === 'Height30' ? 30 : type === 'Height20' ? 20 : 50 })
}
export default {
  name: 'VirtualList-test',
  data () {
    return {
      data: d
    }
  },
  components: {
    VirtualList,
    Height20,
    Height30,
    Height50
  }
}
</script>

<style>

.render-show {
  display: flex;
  justify-content: center;
}
.render-show > div{
  width:500px;
  margin-top:40px;
}
.render-list-item {
  color: #555;
  box-sizing: border-box;
  border-bottom: 1px solid #999;
  box-sizing: border-box;
}
</style>

場景三、以上兩種情況追加數(shù)據(jù)

以上兩種場景。如何追加數(shù)據(jù)呢?

   scrollEvent () {
      // 當(dāng)前滾動位置
      const scrollTop = this.$refs.list.scrollTop
      // 此時的開始索引
      this.start = this.getStart(scrollTop)
      // 此時的結(jié)束索引
      this.end = this.start + this.visibleCount
      const offsetHeight = scrollTop - (this.visibleData.reduce((acc, curVal) => acc + curVal.height, 0) - this.screenHeight)
      // 此時的偏移量
      this.startOffset = offsetHeight < 0 ? 0 : offsetHeight
      // 追加數(shù)據(jù)
      if (this.end + 1 >= this.listData.length) {
        this.$emit('appendData', this.listData.length)
      }
    }
    
    ...
    appendData (start) {
      const d = []
      for (let i = start; i < start + 10; i++) {
        const type = i % 3 === 0 ? i % 2 === 0 ? 'Height30' : 'Height50' : 'Height20'
        d.push({ id: i, value: i, type: type, height: type === 'Height30' ? 30 : type === 'Height20' ? 20 : 50 })
      }
      this.data = [...this.data, ...d]
    }

注意事項

以上兩個場景中均對偏移量做了處理

      this.startOffset = scrollTop - (scrollTop % this.itemSize)
const offsetHeight = scrollTop - (this.visibleData.reduce((acc, curVal) => acc + curVal.height, 0) - this.screenHeight)
      // 此時的偏移量
      this.startOffset = offsetHeight < 0 ? 0 : offsetHeight

真實的滾動就是滾動條滾動了多少,可視區(qū)就向上移動多少。但虛擬滾動不是的。當(dāng)起始索引發(fā)生變化時,渲染數(shù)據(jù)發(fā)生變化了,但渲染數(shù)據(jù)的高度不是連續(xù)的,所以需要動態(tài)的設(shè)置偏移量。當(dāng)滾動時起始索引不發(fā)生變化時,此時可以什么也不做,滾動顯示的內(nèi)容由瀏覽器控制。

以上就是不同場景下Vue中虛擬列表實現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于vue虛擬列表的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Vue中計算屬性未生效:原因、排查與解決過程

    Vue中計算屬性未生效:原因、排查與解決過程

    本文將深入探討計算屬性未生效的常見原因,并提供排查和解決方法,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • uniapp使用uview的簡單案例

    uniapp使用uview的簡單案例

    uView是uni-app生態(tài)專用的UI框架,uni-app?是一個使用?Vue.js?開發(fā)所有前端應(yīng)用的框架,這篇文章主要給大家介紹了關(guān)于uniapp使用uview的簡單案例,需要的朋友可以參考下
    2023-03-03
  • vue post application/x-www-form-urlencoded如何實現(xiàn)傳參

    vue post application/x-www-form-urlencoded如何實現(xiàn)傳參

    這篇文章主要介紹了vue post application/x-www-form-urlencoded如何實現(xiàn)傳參問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • Vue3實現(xiàn)掛載全局方法和用getCurrentInstance代替this

    Vue3實現(xiàn)掛載全局方法和用getCurrentInstance代替this

    這篇文章主要介紹了Vue3實現(xiàn)掛載全局方法和用getCurrentInstance代替this,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • tdesign vue初始化組件源碼解析

    tdesign vue初始化組件源碼解析

    這篇文章主要為大家介紹了tdesign vue初始化組件源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • vue組件Prop傳遞數(shù)據(jù)的實現(xiàn)示例

    vue組件Prop傳遞數(shù)據(jù)的實現(xiàn)示例

    本篇文章主要介紹了vue組件Prop傳遞數(shù)據(jù)的實現(xiàn)示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • Vue + Node.js + MongoDB圖片上傳組件實現(xiàn)圖片預(yù)覽和刪除功能詳解

    Vue + Node.js + MongoDB圖片上傳組件實現(xiàn)圖片預(yù)覽和刪除功能詳解

    這篇文章主要介紹了Vue + Node.js + MongoDB圖片上傳組件實現(xiàn)圖片預(yù)覽和刪除功能,結(jié)合實例形式詳細(xì)分析了Vue + Node.js + MongoDB基于圖片上傳組件實現(xiàn)圖片預(yù)覽和刪除功能相關(guān)操作技巧,需要的朋友可以參考下
    2020-04-04
  • Vue實現(xiàn)紅包雨小游戲的示例代碼

    Vue實現(xiàn)紅包雨小游戲的示例代碼

    紅包也叫壓歲錢,是過農(nóng)歷春節(jié)時長輩給小孩兒用紅紙包裹的禮金。本文將通過Vue制作一個紅包雨小游戲,文中的示例代碼講解詳細(xì),需要的可以參考一下
    2022-01-01
  • vue3使用defineModel實現(xiàn)父子組件雙向綁定

    vue3使用defineModel實現(xiàn)父子組件雙向綁定

    這篇文章主要個給大家介紹了在vue3中使用defineModel進行父子組件中的雙向綁定,文中通過代碼示例給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-01-01
  • vue實現(xiàn)右鍵彈出菜單

    vue實現(xiàn)右鍵彈出菜單

    這篇文章主要為大家詳細(xì)介紹了vue實現(xiàn)右鍵彈出菜單,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-07-07

最新評論

马山县| 洛宁县| 吴旗县| 大港区| 从江县| 满洲里市| 富川| 筠连县| 封开县| 塘沽区| 翼城县| 昂仁县| 正蓝旗| 哈巴河县| 泰安市| 渭源县| 玉环县| 鹿邑县| 河源市| 和林格尔县| 徐州市| 萝北县| 华坪县| 登封市| 涿州市| 长白| 宣城市| 揭阳市| 辽阳市| 邢台县| 岳西县| 吉木萨尔县| 衡南县| 西宁市| 同江市| 进贤县| 新建县| 仁布县| 兰考县| 赤峰市| 锦州市|