前端如何优雅的展示10万条数据?

假设现在后端给你 10 万条数据,你如何优雅的展示在页面?

虽然现实中这种是不可能的,但是有些面试会问,这里个人总结了八种方法,但是我只写了五种

  1. 准备工作,简单搞个架子
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        padding: 0;
        margin: 0;
      }
      #box {
        height: 180vh;
      }
      .item {
        display: flex;
        padding: 10px;
      }
      img {
        width: 150px;
        height: 150px;
      }
    </style>
  </head>
  <body>
    <div id="box"></div>
    <script></script>
  </body>
</html>

下面先写一个获取数据的函数

function getList() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      try {
        let list = []
        for (var i = 0; i < 100000; i++) {
          list.push({
            img: './img.webp',
            text: '我是' + i + 1 + '号嘉宾',
            num: i + 1,
          })
        }
        resolve(list)
      } catch (err) {
        reject()
      }
    }, 10)
  })
}
// 页面容器
const box = document.getElementById('box')

方法一

最简单粗暴的方式直接渲染 以我电脑的配置要渲染十多秒,浏览器还很卡,走路都不利索
getList().then((res) => {
  console.time('开始时间')
  res.forEach((item) => {
    var div = document.createElement('div')
    div.className = 'box'
    div.innerHTML = `<img src="${item.img}" /><span>${item.text}</span>`
    box.appendChild(div)
  })
  console.timeEnd('结束时间')
})

方法二

setTimeout 分页渲染,分批渲染,这样可以提高首屏的加载速度,(异步队列渲染,提高渲染速度和性能,用户几乎感觉不到,浏览器也不卡了,腰也不痛了走路也利索了,嘿嘿)
const randerDom = async () => {
  console.time('开始时间')
  var list = await getList()
  var total = list.length // 计算总记录数
  var page = 0 // 页码
  var pageSize = 100 // 每页展示多少页
  var totalPage = Math.ceil(total / pageSize) // 计算总页数

  var rander = (page) => {
    if (page >= totalPage) return console.timeEnd('结束时间')
    setTimeout(() => {
      for (var i = page * pageSize; i < page * pageSize + pageSize; i++) {
        var item = list[i]
        var div = document.createElement('div')
        div.className = 'item'
        div.innerHTML = `<img src="${item.img}" /><span>${item.text}</span>`
        box.appendChild(div)
      }
      rander(page + 1)
    }, 0)
  }
  rander(page)
}
randerDom()

方法三 使用 requestAnimationFrame 来代替定时器 湿滑的一批

为什么要用 requestAnimationFrame 代替定时器,setTimeout:通过设定间隔时间来不断改变图像位置,达到动画效果。但是容易出现卡顿、抖动的现象;原因是:1、settimeout 任务被放入异步队列,只有当主线程任务执行完后才会执行队列中的任务,因此实际执行时间总是比设定时间要晚;2、settimeout 的固定时间间隔不一定与屏幕刷新时间相同,会引起丢帧。requestAnimationFrame 的优势, requestAnimationFrame:优势:由系统决定回调函数的执行时机。60Hz 的刷新频率,那么每次刷新的间隔中会执行一次回调函数,不会引起丢帧,不会卡顿,内容过多,有兴趣可以去这篇文章靠看看 点这里点这里,嘿嘿 , 不废话了,上代码
const randerDom1 = async () => {
  console.time('开始时间')
  var list = await getList()
  var total = list.length // 计算总记录数
  var page = 0 // 页码
  var pageSize = 100 // 每页展示多少页
  var totalPage = Math.ceil(total / pageSize) // 计算总页数

  var rander = (page) => {
    if (page >= totalPage) return console.timeEnd('结束时间')
    // 使用requestAnimationFrame代替setTimeout
    window.requestAnimationFrame(() => {
      for (var i = page * pageSize; i < page * pageSize + pageSize; i++) {
        var item = list[i]
        var div = document.createElement('div')
        div.className = 'item'
        div.innerHTML = `<img src="${item.img}" /><span>${item.text}</span>`
        box.appendChild(div)
      }
      rander(page + 1)
    }, 0)
  }
  rander(page)
}
randerDom1()

方法四 使用文档碎片+requestAnimationFrame 的方式实现, 这个就有水平多了

前面的方法都是创建一次就插入一次, 这样每次都会重排(回流)重绘,用文档碎片就不一样了,可以先把 1 页的 div 标签先放进文档碎片中,然后一次性 appendChild 到 container 中,这样减少了 appendChild 的次数,极大提高了性能
console.time('开始时间')
var list = await getList()
var total = list.length // 计算总记录数
var page = 0 // 页码
var pageSize = 100 // 每页展示多少页
var totalPage = Math.ceil(total / pageSize) // 计算总页数

var rander = (page) => {
  if (page >= totalPage) return console.timeEnd('结束时间')
  // 使用requestAnimationFrame代替setTimeout
  window.requestAnimationFrame(() => {
    const fragment = document.createDocumentFragment()
    for (var i = page * pageSize; i < page * pageSize + pageSize; i++) {
      var item = list[i]
      var div = document.createElement('div')
      div.className = 'item'
      div.innerHTML = `<img src="${item.img}" /><span>${item.text}</span>`
      fragment.appendChild(div)
    }
    box.appendChild(fragment)
    rander(page + 1)
  }, 0)
}
rander(page)

方法五 滚动懒加载+文档碎片, 都是大佬 就不搞注释了

var pageObj = {
  total: 0,
  page: 0,
  pageSize: 100,
  totalPage: 0,
}
// 获取数据
var data = []
// 分页取出数据进行渲染
const getData = () => {
  var page = pageObj.page * pageObj.pageSize
  var pageSize = page + pageObj.pageSize
  var list = data.slice(page, pageSize)
  const fragment = document.createDocumentFragment()
  list.forEach((item) => {
    var div = document.createElement('div')
    div.className = 'item'
    div.innerHTML = `<img src="${item.img}" /><span>${item.text}</span>`
    fragment.appendChild(div)
  })
  box.appendChild(fragment)
}

getList().then((res) => {
  data = res
  pageObj.total = res.length
  pageObj.totalPage = Math.ceil(pageObj.total / pageObj.pageSize)
  // 数据获取成功先调一次渲染
  getData()
})
// 判断是否触底
window.addEventListener('scroll', function(event) {
  var scrollTop =
    document.documentElement.scrollTop ||
    window.pageYOffset ||
    document.body.scrollTop
  if (
    document.documentElement.scrollHeight <=
    document.documentElement.clientHeight + scrollTop
  ) {
    if (pageObj.page == pageObj.totalPage) return
    // 触底后加载下一页数据
    pageObj.page++
    getData()
  }
})

方法六

手动分页列表 就跟pc端管理系统那种tab分页, 这里就不写了,

方法七

滚动懒加载, 之前的几种方式最终dom上都会有10w个div 这样页面会变得很卡,目标: 页面上只放固定的100或者200个div,div数量不变, 只切换数据 ,page的计算 删除那些数据,添加那些数据 (还没有思路)

方法八

虚拟dom 这个,这个 ,这 , 这, 先把vue的虚拟dom搞明白在来研究这个
  • 1
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值