后端一次性传了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树中。

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

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


目录
相关文章
|
15天前
|
前端开发 JavaScript 关系型数据库
从前端到后端:构建现代化Web应用的技术探索
在当今互联网时代,Web应用的开发已成为了各行各业不可或缺的一部分。从前端到后端,这篇文章将带你深入探索如何构建现代化的Web应用。我们将介绍多种技术,包括前端开发、后端开发以及各种编程语言(如Java、Python、C、PHP、Go)和数据库,帮助你了解如何利用这些技术构建出高效、安全和可扩展的Web应用。
|
17天前
|
前端开发 小程序 Java
uniapp上传图片 前端以及java后端代码实现
uniapp上传图片 前端以及java后端代码实现
31 0
|
19天前
|
前端开发 JavaScript 网络协议
前端最常见的JS面试题大全
【4月更文挑战第3天】前端最常见的JS面试题大全
40 5
|
1月前
|
JSON 前端开发 Java
layui上传图片,前端直接拷代码,后端……
layui上传图片,前端直接拷代码,后端……
29 0
|
7天前
|
存储 缓存 前端开发
前端如何利用indexDB进行数据优化
使用IndexedDB作为浏览器内置的客户端数据库,用于存储大量数据和实现离线支持。它能缓存常用数据,减少服务器请求,提高用户体验。IndexedDB支持数据索引、复杂查询及版本管理,允许离线操作并同步到服务器。但需熟悉其异步API,可借助Dexie.js、localForage等库简化使用。
|
12天前
|
小程序 前端开发 JavaScript
小程序全栈开发:前端与后端的完美结合
【4月更文挑战第12天】本文介绍了小程序全栈开发,涵盖前端和后端的关键点。前端使用WXML和WXSS进行页面结构和样式设计,JavaScript处理逻辑及组件使用;后端采用Node.js等语言处理业务逻辑、数据库设计和API接口开发。前端与后端通过数据交互实现结合,采用前后端分离模式,支持跨平台运行。调试测试后,提交微信审核并上线运营。掌握前端后端结合是小程序成功的关键。
|
12天前
|
Web App开发 移动开发 运维
跨域解决方案[前端+后端]
跨域解决方案[前端+后端]
23 0
|
13天前
|
JavaScript 前端开发 API
游戏开发入门:Python后端与Vue前端的协同工作方式
【4月更文挑战第11天】使用Python后端(Flask或Django)和Vue.js前端开发游戏变得流行,能提高开发效率和可维护性。本文指导如何构建这样的项目,包括设置环境、创建虚拟环境、搭建后端API及前端Vue组件,强调前后端协作和API接口的重要性。这种架构促进团队合作,提升代码质量和游戏体验。
|
14天前
|
供应链 JavaScript 前端开发
使用Django和Vue实现电子商务网站的后端和前端
【4月更文挑战第10天】本文介绍了使用Django和Vue构建电子商务网站的后端与前端方法。Django作为Python的Web框架负责后端,其模型-视图-控制器设计简化了商品管理、购物车和订单处理。Vue.js用于前端,提供数据驱动和组件化的用户界面。通过定义Django模型和视图处理请求,结合Vue组件展示商品和管理购物车,开发者可构建交互性强的电商网站。虽然实际开发涉及更多细节,但本文为入门提供了基础指导。
|
27天前
|
前端开发 JavaScript NoSQL
从前端到后端:构建全栈应用的技术挑战与解决方案
在当今互联网时代,全栈开发成为越来越受欢迎的技术趋势。本文将深入探讨从前端到后端的全栈开发过程中所面临的技术挑战,并提出相应的解决方案,涵盖前端框架选择、后端技术架构、数据库设计以及跨平台兼容性等关键问题。