前端性能优化之:虚拟列表+网络请求数据

<!DOCTYPE html>
<html>
  <head lang="en">
    <meta charset="UTF-8" />
    <title>前端虚拟列表</title>
    <style type="text/css" rel="stylesheet">
      .content {
        background-color: rgb(250, 172, 94);
        overflow: auto;
        padding: 0 20px;
        width: 100vw;
        height: 50vh;
        box-sizing: border-box;
      }
      .target {
        height: 100%;
        box-sizing: border-box;
      }
    </style>
  </head>
  <body>
    <div class="content">
      <ul class="target"></ul>
    </div>
    <div class="loading"></div>
  </body>

  <script>
    let loading = false

    let datalength = 20 //每页显示数据

    let data = [] //总共的1000条数据,数组存放

    let showdata = [] //要去渲染的n条数据,数组存放

    let itemHeight = "50"

    let topdistance = 0 //记录偏移量

    let first //首条数据的索引

    let end //尾条数据索引

    for (let i = 0; i < 20; i++) {
      data.push(i)
    }
    let div = document.querySelector(".content")
    let ul = document.querySelector("ul")
    let load = document.querySelector(".loading")

    //首屏渲染数据
    myscroll()
    // 使用防抖函数,降低性能开销,但是会存在抖动问题
    // div.onscroll = debounce(myscroll, 50)
    // 不使用防抖函数
    div.onscroll = myscroll
    // 防抖函数
    function debounce(fn, delay) {
      let timer = null

      return function (...args) {
        if (timer) {
          clearTimeout(timer)
        }

        timer = setTimeout(() => {
          fn.apply(this, args)
        }, delay)
      }
    }
    function onScrollDown() {
      if (loading) return
      return new Promise(resolve => {
        loading = true
        load.innerHTML = "模拟请求数据 数据加载中..."
        setTimeout(() => {
          let max = data[data.length - 1]
          for (let i = max + 1; i < max + 20; i++) {
            data.push(i)
          }
          loading = false
          load.innerHTML = ""
          resolve()
        }, 2000)
      })
    }

    async function myscroll(e) {
      if (
        end >= data.length - 1 &&
        e &&
        e.target.scrollHeight - e.target.scrollTop - e.target.clientHeight <=
          itemHeight
      ) {
        //触底时进行操作
        await onScrollDown()
      }

      //手动设置偏移量
      topdistance = e ? e.target.scrollTop : 0

      //将偏移量赋值给paddingTop,注意上述css盒模型如果不设置一下的话,会导致滚轮一直下滑,ul的高度会被无限拉长
      ul.style.paddingTop = topdistance - (topdistance % itemHeight) + "px"

      //根据偏移量判断该从哪里截取
      let index = Math.floor(topdistance / itemHeight)
      first = index
      end = index + datalength

      //对原生数据进行截取
      showdata = data.slice(first, end)

      //下面操作dom节点进行渲染
      const dom = document.createDocumentFragment()
      let li = null
      showdata.map(item => {
        li = document.createElement("li")
        li.style.height = itemHeight + "px"
        li.style.fontSize = "30px"
        li.innerHTML = item
        dom.appendChild(li)
      })

      //每次渲染之前清空内容
      ul.innerHTML = ""
      ul.appendChild(dom)
    }
  </script>
</html>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容