后端一次性传了10w条数据,前端该如何处理?—— 面试高频

简介: 笔记

13.png

1. 这道题在考什么?


  • 对于性能优化的处理方案
  • 对于前端渲染机制的了解
  • 极端情况下的处理及知识领域的广度


2.先用 node.js 整个10w条数据


const http = require('http')
const PORT = 8000
const server = http.createServer((req, res) => {
    res.writeHead(200, {
        //设置允许跨域的域名,也可设置*允许所有域名
        'Access-Control-Allow-Origin': '*',
        //跨域允许的请求方法,也可设置*允许所有方法
        "Access-Control-Allow-Methods": "DELETE,PUT,POST,GET,OPTIONS",
        //允许的header类型
        'Access-Control-Allow-Headers': 'Content-Type'
    })
    let list = [];
    let num = 0;
    for (let i = 0; i < 200; i++) {
        num++;
        list.push({
            src: '图片地址',
            text: `这是第${num}张图片`,
            id: num
        })
    }
    res.end(JSON.stringify(list))
})
server.listen(PORT, () => {
    console.log('服务跑起来了!')
})


3. 基础代码环境


  • index.html 代码如下
<!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>
        img{
            width: 150px;
        }
        .flex{
            display: flex;
            padding: 10px;
        }
    </style>
</head>
<body>
    <div id="container">
    </div>
    <script src="./index.js"></script>
</body>
</html>
  • index.js 代码如下
const getList = () => {
    return new Promise((resolve, reject) => {
      // 创建请求
        let ajax = new XMLHttpRequest();
        // 这里请求的是本地服务器
        ajax.open('get', 'http://127.0.0.1:8000');
        ajax.send();
        ajax.onreadystatechange = function(){
            if(ajax.readyState == 4 && ajax.status == 200){
                resolve(JSON.parse(ajax.responseText))
            }
        }
    })
}
const container = document.getElementById("container")

4. 常规处理方案


const renderList = async () => {
    console.time('列表时间')
    const list = await getList();
    list.forEach( item => {
        const div = document.createElement('div')
        div.className = 'flex'
        div.innerHTML = `<img src="${item.src}" /><span>${item.text}</span>`
        container.appendChild(div)
    });
    console.timeEnd('列表时间')
}
renderList()
  • 这种方案就是简单粗暴的循环渲染
  • 此方案耗时大概是 13s
  • 这种做法当然是不可取的,等到天都黑了,用户可能会骂娘


5. 优化的第一种方式 —— 前端分页


const renderList = async () => {
    console.time('列表时间')
    const list = await getList();
    const total = list.length;
    const page = 0;
    const limit = 200;
    // 总页数
    const totalPage = Math.ceil(total / limit);   // Math.ceil 1.1 => 2
    const render = (page) => {
        if(page >= totalPage) return
        setTimeout(() => {
            for(let i = page * limit; i < page * limit + limit; i++){
                const item = list[i];
                const div = document.createElement('div')
                div.className = 'flex'
                div.innerHTML = `<img src="${item.src}" /><span>${item.text}</span>`
                container.appendChild(div)
            }
            render(page + 1)
        }, 0)
    }
    render(page);
    console.timeEnd('列表时间')
}
renderList()
  • 思路是把十万条数据分成 10w / 200页,再加上setTimeout,每次渲染一页,速度得到了大幅度提升
  • 方案耗时:不到 1s 搞定


6. 再次优化


const renderList = async () => {
    console.time('列表时间')
    const list = await getList();
    const total = list.length;
    const page = 0;
    const limit = 200;
    // 总页数
    const totalPage = Math.ceil(total / limit);   // Math.ceil 1.1 => 2
    const render = (page) => {
        if(page >= totalPage) return
        requestAnimationFrame(() => {
            for(let i = page * limit; i < page * limit + limit; i++){
                const item = list[i];
                const div = document.createElement('div')
                div.className = 'flex'
                div.innerHTML = `<img src="${item.src}" /><span>${item.text}</span>`
                container.appendChild(div)
            }
            render(page + 1)
        })
    }
    render(page);
    console.timeEnd('列表时间')
}
renderList()
  • 使用 requestAnimationFrame 代替 setTimeout,减少了重排的次数,极大提高了性能


7. 极致优化(最佳方案)


const renderList = async () => {
    console.time('列表时间')
    const list = await getList();
    const total = list.length;
    const page = 0;
    const limit = 200;
    // 总页数
    const totalPage = Math.ceil(total / limit);   // Math.ceil 1.1 => 2
    const render = (page) => {
        if(page >= totalPage) return
        requestAnimationFrame(() => {
            const fragment = document.createDocumentFragment()
            // 文档碎片 => dom节点 不是在dom树上一部分
            // N次追加 => 1次
            for(let i = page * limit; i < page * limit + limit; i++){
                const item = list[i];
                const div = document.createElement('div')
                div.className = 'flex'
                div.innerHTML = `<img src="${item.src}" /><span>${item.text}</span>`
                fragment.appendChild(div)
                // container.appendChild(div)
            }
            container.appendChild(fragment)
            render(page + 1)
        })
    }
    render(page);
    console.timeEnd('列表时间')
}
renderList()
  • 这里的优化点主要是:之前是创建一个div就追加一次:
div.innerHTML = `<img src="${item.src}" /><span>${item.text}</span>`
container.appendChild(div)
  • 现在改成一次性追加,极大的提高了性能。
container.appendChild(fragment)


8. 知识点补充


window.requestAnimationFrame()

告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。

该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行;

若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用window.requestAnimationFrame()

DocumentFragments —— 文档碎片

DocumentFragments 是DOM节点。它们不是主DOM树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。

在DOM树中,文档片段被其所有的子元素所代替。因为文档片段存在于内存中,并不在DOM树中。

所以将子元素插入到文档片段时不会引起【页面回流】(对元素位置和几何上的计算)。

因此,使用文档片段通常会带来更好的性能。


目录
相关文章
|
存储 前端开发 安全
前端如何存储数据:Cookie、LocalStorage 与 SessionStorage 全面解析
本文全面解析前端三种数据存储方式:Cookie、LocalStorage与SessionStorage。涵盖其定义、使用方法、生命周期、优缺点及典型应用场景,帮助开发者根据登录状态、用户偏好、会话控制等需求,选择合适的存储方案,提升Web应用的性能与安全性。(238字)
602 0
|
7月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
存储 监控 安全
前端框架的数据驱动方式如何保证数据的安全性?
总之,前端框架的数据驱动方式需要综合运用多种手段来保证数据的安全性。从传输、存储、访问控制到防范攻击等各个方面进行全面考虑和实施,以确保用户数据的安全可靠。同时,不断加强安全管理和技术创新,以应对不断变化的安全挑战。
458 60
|
9月前
|
前端开发 Cloud Native Java
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
|
10月前
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
710 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
11月前
|
NoSQL 关系型数据库 MySQL
招行面试:高并发写,为什么不推荐关系数据?
资深架构师尼恩针对高并发场景下为何不推荐使用关系数据库进行数据写入进行了深入剖析。文章详细解释了关系数据库(如MySQL)在高并发写入时的性能瓶颈,包括存储机制和事务特性带来的开销,并对比了NoSQL数据库的优势。通过具体案例和理论分析,尼恩为读者提供了系统化的解答,帮助面试者更好地应对类似问题,提升技术实力。此外,尼恩还分享了多个高并发系统的解决方案及优化技巧,助力开发者在面试中脱颖而出。 文章链接:[原文链接](https://mp.weixin.qq.com/s/PKsa-7eZqXDg3tpgJKCAAw) 更多技术资料和面试宝典可关注【技术自由圈】获取。
|
10月前
|
缓存 安全 Java
面试中的难题:线程异步执行后如何共享数据?
本文通过一个面试故事,详细讲解了Java中线程内部开启异步操作后如何安全地共享数据。介绍了异步操作的基本概念及常见实现方式(如CompletableFuture、ExecutorService),并重点探讨了volatile关键字、CountDownLatch和CompletableFuture等工具在线程间数据共享中的应用,帮助读者理解线程安全和内存可见性问题。通过这些方法,可以有效解决多线程环境下的数据共享挑战,提升编程效率和代码健壮性。
369 6
|
存储 Java easyexcel
招行面试:100万级别数据的Excel,如何秒级导入到数据库?
本文由40岁老架构师尼恩撰写,分享了应对招商银行Java后端面试绝命12题的经验。文章详细介绍了如何通过系统化准备,在面试中展示强大的技术实力。针对百万级数据的Excel导入难题,尼恩推荐使用阿里巴巴开源的EasyExcel框架,并结合高性能分片读取、Disruptor队列缓冲和高并发批量写入的架构方案,实现高效的数据处理。此外,文章还提供了完整的代码示例和配置说明,帮助读者快速掌握相关技能。建议读者参考《尼恩Java面试宝典PDF》进行系统化刷题,提升面试竞争力。关注公众号【技术自由圈】可获取更多技术资源和指导。
|
存储 缓存 关系型数据库
滴滴面试:单表可以存200亿数据吗?单表真的只能存2000W,为什么?
40岁老架构师尼恩在其读者交流群中分享了一系列关于InnoDB B+树索引的面试题及解答。这些问题包括B+树的高度、存储容量、千万级大表的优化、单表数据量限制等。尼恩详细解释了InnoDB的存储结构、B+树的磁盘文件格式、索引数据结构、磁盘I/O次数和耗时,以及Buffer Pool缓存机制对性能的影响。他还提供了实际操作步骤,帮助读者通过元数据找到B+树的高度。尼恩强调,通过系统化的学习和准备,可以大幅提升面试表现,实现“offer直提”。相关资料和PDF可在其公众号【技术自由圈】获取。
|
JavaScript API 开发工具
<大厂实战场景> ~ Flutter&鸿蒙next 解析后端返回的 HTML 数据详解
本文介绍了如何在 Flutter 中解析后端返回的 HTML 数据。首先解释了 HTML 解析的概念,然后详细介绍了使用 `http` 和 `html` 库的步骤,包括添加依赖、获取 HTML 数据、解析 HTML 内容和在 Flutter UI 中显示解析结果。通过具体的代码示例,展示了如何从 URL 获取 HTML 并提取特定信息,如链接列表。希望本文能帮助你在 Flutter 应用中更好地处理 HTML 数据。
448 1

热门文章

最新文章

  • 1
    前端如何存储数据:Cookie、LocalStorage 与 SessionStorage 全面解析
    602
  • 2
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(九):强势分析Animation动画各类参数;从播放时间、播放方式、播放次数、播放方向、播放状态等多个方面,完全了解CSS3 Animation
    252
  • 3
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(八):学习transition过渡属性;本文学习property模拟、duration过渡时间指定、delay时间延迟 等多个参数
    234
  • 4
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(七):学习ransform属性;本文学习 rotate旋转、scale缩放、skew扭曲、tanslate移动、matrix矩阵 多个参数
    186
  • 5
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(六):全方面分析css的Flex布局,从纵、横两个坐标开始进行居中、两端等元素分布模式;刨析元素间隔、排序模式等
    293
  • 6
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(五):背景属性;float浮动和position定位;详细分析相对、绝对、固定三种定位方式;使用浮动并清除浮动副作用
    424
  • 7
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(四):元素盒子模型;详细分析边框属性、盒子外边距
    183
  • 8
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(三):元素继承关系、层叠样式规则、字体属性、文本属性;针对字体和文本作样式修改
    126
  • 9
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(二):CSS伪类:UI伪类、结构化伪类;通过伪类获得子元素的第n个元素;创建一个伪元素展示在页面中;获得最后一个元素;处理聚焦元素的样式
    196
  • 10
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(一):CSS发展史;CSS样式表的引入;CSS选择器使用,附带案例介绍
    273