海量数据渲染的解决方案

创建一个十万条数据的接口

这个自己用mock模拟或者自己用nodejs的express直接写一下就是核心代码如下

js 复制代码
route.get("/bigData", (req, res) => {
    res.header('Access-Control-A1low-origin', "*"); 
    //允许時域
    let arr=[] // 定义数组,存放十万条数据
    for(let i = 0; i < 10000; i++){ /循环添加十万条数据
        arr.push({
            id: i+1,
            name: '名字' + (i + 1),
            value: i + 1,
        })
    res.send({ code: 0, msg: "成功", data: arr } //将十万茶数据返回
})

这里贴出后端的接口 http://124.223.69.156:3300/bigData

第一种直接渲染十万条数据

js 复制代码
async load() {
    this.loading = true;
    const res = await axios.get("http://ashuai.work: 1000/bigData");
    this.arr = res.data.data;
    this.loading = false;
}

这种肯定是不行的会导致页面直接卡死

第二种使用定时器分组分批分堆依次渲染 (定时加载分堆的思想)

  1. 前端请求到十万条数据以后,先不着急渲染,先将10万条数据分堆分批次
  2. 例如一堆放10条数据,那么十万条就有一万堆
  3. 使用定时器,一次渲染一堆,渲染一万次就可以了
js 复制代码
// 分堆
for (let i=0;i < twoArr.length;i++){
    setTimeout(()=>{
        this.arr = [ ...this.arr, ...twoArr[i]]
    },20*i)
}

使用requestAnimationFrame 进行优化

解释 比如浏览器每16.7ms刷新一次,动画回调也每167ms调用一一次,这样就不会存在过度绘制的问题,动画不会掉帧,自然流畅。

js 复制代码
// 1. 对数据进行分堆处理
let twoArr = this.averageFn(res,data . data)
// 2. 定义一个泣染函数
const useTwoArr = (page)=> {
    if(page > twoArr.length){
      return
    }
    // 3. 用动画请求帧来优化
    requestAnimationFrame(()=>{
    // 4. 取出一项,拼接一项
        this.arr = [...this.arrs, ...twoArr[page]] 
       // 5. 这项进行下项
        page = page + 1
        // 6. 递归调用这个图数
        useTwoArr(page)
    })
}

表格滚动触底加载(滚动到底,再加载一堆)

js 复制代码
import elTableInfinitescroll from "el-table- infinite-scroll";
vue.use(elTableInfinitescroll);
js 复制代码
async load() {
console.1og("自动多次执行之,肖次执行会根据高度去计算要执行几次合适");
// 第1步,触底加载相当于把二维数組的每一项收出来用,用光时return停止即可
if (this.allTableData.length == 0) {
console.log("没数据啦");
return; 
}
//第二步,加载的时候,把二维数组的第一项收出来,拼接到要展示的表格数据中去
let arr = this.allTableData[0];
this.tableData = this.tableData.concat(arr);
console.log(this.tableData);
// 第三步,拼接展示以后,再把2維数组的第一项的数据删除即可
this.allTableData.shift();

使用无限加载/虚拟列表进行展示

什么是虚拟列表?

所谓的虚拟列表实际上是前端障眼法的一种表现形式。 看到的好像所有的数据都渲染了,实际上.只渲染「可视区域」的部分罢了

有点像我们看电影,我们看的话,是在块电影屏幕上,一秒秒的看(不停的放映)

但是实际上电影有俩小时,如果把两个小时的电影都铺开的话,那得需要多少块电影屏幕呢?

同理,如果10万条数据都渲染,那得需要多少dom节点元素呢?

所以我们只给用户看,他当下能看到的

如果用户要快进或快退(下拉滚动条或者上拉滚动条)

再把对应的内容呈现在电影屏幕上(呈现在可视区域内)

这样就实现了看着像是所有的dom元素每一条 数据都有渲染的障眼法效果

基本思路

  1. 写一个代表可视区域的div容器
  2. 计算可视区域可以显示的数据条数(可视区域的高度/单条数据的高度)
  3. 监听滚动,当滚动条变化时,计算被卷起的高度(scrollTop)
  4. 计算可视区域的起始下标(卷起的高度/单条数据的高度)
  5. 计算可视区域的结束下标(起始下标+可视区域可以显示的数据条数)
  6. 取起始下标到结束下标之间的数据,渲染到页面
  7. 计算起始下标的数据在整个列表中的偏移位置并设置到列表上(transform:translateY())

代码实现

html 复制代码
<!DOCTYPE html>
<html>
  <head>
    <title>js 实现虚拟列表</title>
  </head>
  <style>
    html,
    body {
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
    }
 
    ul,
    li {
      margin: 0;
      padding: 0;
      list-style: none;
    }
 
    .box {
      width: 750px;
      height: 900px;
      overflow-y: auto;
      box-sizing: border-box;
      margin: 0 auto;
    }
 
    .content-area {
      overflow: hidden;
    }
 
    li {
      height: 100px;
      background-color: #eee;
      margin-bottom: 10px;
      display: flex;
      align-items: center;
      padding-left: 20px;
      border-radius: 12px;
      box-sizing: border-box;
    }
  </style>
  <body>
    <!-- 可视区域的高度--用户看见的高度(750px) -->
    <div class="box">
      <!-- 内容真实高度 即200条数据的高度-->
      <div class="content-area" id="contentArea">
        <ul></ul>
      </div>
    </div>
    <script>
      let el = document.querySelector(".box");
      let itemHeight = 110; //每个元素的高度(li的高度100 + marginBottom 10)
      let pageSize = Math.ceil(el.clientHeight / itemHeight); // 获取一个滚动屏最大可容纳子元素个数(向上取整)
      let data = []; //mock数据
      let startIndex = 0; //可视区第一行下标
      let endIndex = pageSize; //可视区最后一行下标
 
      // 初始化模拟数据
      let getData = () => {
        for (let i = 0; i < 200; i++) {
          data.push({
            content: `我是显示的内容${i + 1}`,
          });
        }
      };
 
      // 加载数据并插入到dom页面
      let loadData = () => {
        let html = "";
        let sliceData = data.slice(startIndex, endIndex);
        for (let i = 0; i < sliceData.length; i++) {
          html += `
              <li class="item">
                <p>${sliceData[i].content}</p>
              </li>`;
        }
        el.querySelector("#contentArea ul").innerHTML = html;
      };
 
      // 更新DOM
      let updateHtml = () => {
        let sliceData = data.slice(startIndex, endIndex);
        let itemAll = el.querySelectorAll(".item");
        for (let i = 0; i < sliceData.length; i++) {
          itemAll[i].querySelector("p").innerHTML = sliceData[i].content;
        }
      };
 
      // 滑动监听
      el.addEventListener("scroll", function () {
        let scrollTop = el.scrollTop; // 滚动高度
        startIndex = Math.ceil(scrollTop / itemHeight); // 重新计算开始的下标,div顶部卷起来的长度除以列表元素的高度
        endIndex = startIndex + pageSize;
        updateHtml(); // 重新更新dom
        el.querySelector("#contentArea ul").style.transform =
          "translateY(" + startIndex * itemHeight + "px)";
      });
 
      let init = () => {
        getData();
        loadData();
        document.getElementById("contentArea").style.height =
          itemHeight * data.length + "px"; // 占位dom的高度
      };
 
      // 页面初始化调用
      init();
    </script>
  </body>
</html>
相关推荐
一路向前的月光2 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   2 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web2 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
Jiaberrr3 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
安冬的码畜日常5 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js
太阳花ˉ5 小时前
html+css+js实现step进度条效果
javascript·css·html
john_hjy6 小时前
11. 异步编程
运维·服务器·javascript
风清扬_jd6 小时前
Chromium 中JavaScript Fetch API接口c++代码实现(二)
javascript·c++·chrome
yanlele6 小时前
前瞻 - 盘点 ES2025 已经定稿的语法规范
前端·javascript·代码规范