1、实现思路:渲染大量数据时,合理使用 createDocumentFragment 和 requestAnimationFrame,将操作且分为一小段一小段去执行,从而实现页面不卡顿。
2、createDocumentFragment()
- 是用来创建一个虚拟的节点对象(用来创建文档碎片节点),该节点是一个虚拟节点,不属于文档树。在将 DocumentFragment 节点插入文档树时,插入的并不是 DocumentFragment 节点本身,而是插入他的子节点(后代节点)。
- 当需要添加向页面添加很多DOM元素时(如添加很多个 li 标签),如果一个个createElement出来,然后再一个个appendChild上去,会频繁的操作DOM,很影响性能。因此可以可以使用 DocumentFragment 节点,先把 li 标签添加到 DocumentFragment,然后由 DocumentFragment 统一添加到页面上,会大大减少DOM操作,性能明显提升。(因为插入DocumentFragment节点的时候是插入他的后代节点,所以最后的实现效果是一样的,但性能有很大的提升。)
3、requestAnimationFrame() // 传入一个 回调函数/动画
- 该方法功能类似于 setInterval,但 requestAnimationFrame 在性能方面会更好一些。它不需要设置时间间隔,它的时间间隔由系统定义(一般为16.67ms),保证最佳的绘制效率。
- requestAnimationFrame() 浏览器没刷新一次,回调代码执行一次,不会造成丢帧或卡顿的现象,且当页面最小化或者隐藏时,requestAnimationFrame 会暂停渲染,当页面重新激活时,会从上次暂停的地方继续开始渲染,故性能方面会更好。
4、渲染大量数据不卡顿页面–例子
function renderOfNoStuck(total,parentNode,once){
// total:必选,需要渲染的数据总量(可根据需求自定义)
// parentNode:必选,需要将数据渲染到哪个节点下
// once:可选,分段渲染时一小段渲染的数据量,默认为 20
if (!parentNode || parentNode.nodeType !== 1) {
// nodeType === 1 为元素节点 (2为属性节点,3为文本节点)
throw new TypeError('请传入正确的参数');
}
setTimeout(() => {
// 把大量数据切成一小段一小段进行渲染
const once = once || 20; // 一小段渲染的数据量(可根据需求自定义)
const loopCount = Math.ceil(total / once); // 插入数据需要的次数
let countOfRender = 0; // 已渲染的数据量
// 添加数据的方法
function add() {
const fragment = document.createDocumentFragment(); // 创建一个 DocumentFragment节点
for (let i = 0; i < once; i++) {
const li = document.createElement('li');
li.innerText = Math.floor(Math.random() * total); // 给标签添加内容 (可根据需求自定义)
fragment.appendChild(li); // 把数据先添加到 DocumentFragment 节点上
}
parentNode.appendChild(fragment); // 然后由 DocumentFragment 节点统一渲染到页面
countOfRender += 1;
loop();
}
function loop() {
if (countOfRender < loopCount) {
window.requestAnimationFrame(add); // requestAnimationFrame 传入回调函数add,渲染数据
}
}
loop();
}, 0)
}
// let parentNode = document.querySelector('ul');
// renderOfNoStuck(100000, parentNode)
// renderOfNoStuck(20, parentNode, 1) // 设置成 1 时有一个比较好看的效果