theme: channing-cyan
开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第六天,点击查看活动详情
题引:
当我们在公司做项目的时候,难免会遇到后端接口直接给你返回成千上万的数据进行渲染。如果我们直接一股脑遍历添加的话,就会导致空白页面的等待时间是很长且异常卡顿,那么对于数据过大的渲染就需要进行特殊的处理。这也是一道非常经典的面试题。下面我将介绍几种通用的方法。
正文:
这里提供四种比较常见的解决方案,也是日常开发中用的较多的。
防抖节流
防抖节流这种八股文该怎么使用好像都不需要详细讲解了,直接上代码即可
该防抖函数可以实现 this指向,参数,立即执行,取消功能,返回值
function debounce(fn, delay, immediate = false,resultCall) {
let timer = null;
let isInvoke = false;
let result = null;
//返回给元素的真正函数
const _debounce = function (...args) {
//取消上一次的操作
if (timer) clearTimeout(timer);
//如果是第一次执行或者已经执行完的,采取立即执行
if (immediate && !isInvoke) {
result = fn.apply(this, args);
if(resultCall){
resultCall(result)
}
isInvoke = true;
}
//开启防抖
else {
timer = setTimeout(() => {
//执行真正的函数
result = fn.apply(this, args);
if(resultCall){
resultCall(result)
}
isInvoke = false;
}, delay);
}
};
//取消事件
_debounce.cancel = function () {
if (timer) clearTimeout(timer);
};
return _debounce;
}
该节流函数可以实现 this指向,参数,立即执行,取消功能,返回值
function throttle(fn, interval, options = { leading: true, trailing: false }) {
// 1.记录上一次的开始时间
//leading记录要不要第一次触发,即要不要同步执行
//trailing记录要不要进行定时器触发
const { leading, trailing } = options
let lastTime = 0
let timer = null
// 2.事件触发时, 真正执行的函数
const _throttle = function(...args) {
// 2.1.获取当前事件触发时的时间
const nowTime = new Date().getTime()
//要不要同步执行
if (!lastTime && !leading) lastTime = nowTime
// 2.2.使用当前触发的时间和之前的时间间隔以及上一次开始的时间, 计算出还剩余多长事件需要去触发函数
const remainTime = interval - (nowTime - lastTime)
if (remainTime <= 0) {
if (timer) {
clearTimeout(timer)
timer = null
}
// 2.3.真正触发函数
fn.apply(this, args)
// 2.4.保留上次触发的时间
lastTime = nowTime
return
}
if (trailing && !timer) {
timer = setTimeout(() => {
timer = null
lastTime = !leading ? 0: new Date().getTime()
fn.apply(this, args)
}, remainTime)
}
}
_throttle.cancel = function() {
if(timer) clearTimeout(timer)
timer = null
lastTime = 0
}
return _throttle
}
分页处理
分页处理就不过多描述了,需要和后端进行搭配,接口传递page,size等参数
懒加载
懒加载的话这边需要判断目标标签的offsetTop-scrollTop<clientHeight来决定是否进行数据获取或者渲染,多用于图片懒加载(后面将会专门讲解图片懒加载的实现)。
定时处理
当我们接受到成千上万的数据要进行渲染时,可能较多的人会使用setTineout来作为定时器,通过分块的方式定时渲染。 但使用 requestAnimationFrame的效果是会比 setTimeout 的性能更好,因为使用前者的时候浏览器会帮我们进行处理优化,在最优的时候进行调用,而不是像后者设置一个时间。
另外,让我们往一个dom节点插入数据的时候,我们99%都会使用document.createELement,但是如果在该场景下,你插入一条数据都会造成DOM树的重新渲染,当插入的元素多时会造成没必要的开销。但是我们可以使用document.Fragment来使用,它就像vue里面的template后者react的<></>,不是一个真实的元素标签。
与createElement相比,它最大的好处就是不会触发DOM树的重新渲染,且不会对性能造成影响。因为所有的节点会被一次性插入到文档中,所以仅会发生一个重渲染的操作,而不是每个节点分别被插入到文档中从而发生多次重渲染的操作。
<!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>
</head>
<body>
<ul id="list"></ul>
<script>
const total = 10000;
const num = 20;
const times = Math.ceil(total / num);
let currentNums = 0;
const ul = document.querySelector("#list");
function add() {
let frag = new DocumentFragment();
for (let i = 0; i < num; i++) {
const li = document.createElement("li");
li.innerHTML = Math.floor(i + currentNums * num);
frag.appendChild(li);
}
currentNums++;
ul.appendChild(frag);
if (currentNums < times) {
window.requestAnimationFrame(add);
}
}
window.requestAnimationFrame(add);
</script>
</body>
</html>
结尾:
以上就是对于数据过大的处理方法汇总。
暂无评论内容