优化一次渲染多条数据

一次性渲染数据的优化

当遇到需要一次性向页面插入大量数据的情况,如何才能在不卡页面的情况下渲染数据,一般有两种做法:

1、时间分片;2、虚拟列表

这里只介绍如何使用时间分片来渲染大量数据:

简单粗暴

做法是 一次性渲染

<ul id ="container"></ul>

<script>
// 记录任务开始时间

let now = Date.now();
// 插入十万条数据
const total = 100000;
// 获取容器
let ul =  document.getElementById('container');
// 将数据插入容器中
for( let i = 0; i < total; i++){
    let li = document.createElement('li');
    li.innerText =~~(Math.random()* total)
    ul.appendChild(li);
}
console.log('JS运行时间:',Date.now()- now);
setTimeout(()=>{
  console.log('总运行时间:',Date.now()- now);
},0)
// print: JS运行时间:187
// print: 总运行时间:2844
</script>

第一个 console.log的触发时间是在页面进行渲染之前,此时得到的间隔时间为JS运行所需要的时间

第二个 console.log是放到 setTimeout 中的,它的触发时间是在渲染完成才执行的

依照两次 console.log的结果,可以得出结论:

对于大量数据渲染的时候,JS运算并不是性能的瓶颈,性能的瓶颈主要在于渲染阶段

使用定时器优化

页面的卡顿是由于同时渲染大量DOM所引起的,所以我们考虑将渲染过程分批进行

在这里,我们使用 setTimeout来实现分批渲染:

<ul id="container"></ul>
<script>
//需要插入的容器
let ul = document.getElementById('container');
// 插入十万条数据
let total = 100000;
// 一次插入 20 条
let once = 20;
//总页数
let page =  total/once
//每条记录的索引
let index =  0;
//循环加载数据
function loop(curTotal,curIndex){
if(curTotal <= 0){        
	return false;    
}   
//每页多少条
let pageCount = Math.min(curTotal , once);

    setTimeout(()=>{        
		for(let i = 0; i < pageCount; i++){
            let li =  document.createElement('li');
            li.innerText =  curIndex + i+' : '+~~(Math.random()* total)
            ul.appendChild(li)        
    	 }
     	loop(curTotal - pageCount,curIndex + pageCount)
	},0)}

loop(total,index);
</script>

这种方法页面加载的时间变快了,每次刷新时可以很快的看到第一屏的所有数据,但是当我们快速滚动页面的时候,会发现页面出现闪屏或白屏的现象

使用 requestAnimationFrame

与 setTimeout相比, requestAnimationFrame最大的优势是由系统来决定回调函数的执行时机。

requestAnimationFrame的步伐跟着系统的刷新步伐走。它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次,这样就不会引起丢帧现象。

我们使用 requestAnimationFrame来进行分批渲染:

<ul id="container"></ul>
<script>
 	//需要插入的容器
	let ul =  document.getElementById('container');

	// 插入十万条数据
	let total = 100000;
	// 一次插入 20 条
	let once = 20;
   //总页数
	let page =  total/once
	//每条记录的索引
	let index = 0;
	//循环加载数据
	function loop(curTotal,curIndex){
			if(curTotal <= 0){        
				return false;
			}
			//每页多少条
			let pageCount = Math.min(curTotal, once);
			window.requestAnimationFrame(function(){
					for(let i = 0; i< pageCount; i++){
            			let li = document.createElement('li');
            			li.innerText = curIndex + i +' : '+~~(Math.random()* total)
						 ul.appendChild(li)
					}loop(curTotal - pageCount,curIndex +pageCount)
		})}

loop(total,index);
</script>

页面加载的速度很快,并且滚动的时候,也很流畅没有出现闪烁丢帧的现象。

使用 DocumentFragment

DocumentFragments是DOM节点,但并不是DOM树的一部分,可以认为是存在内存中的,所以将子元素插入到文档片段时不会引起页面回流。

当 append元素到 document中时,被 append进去的元素的样式表的计算是同步发生的,此时调用 getComputedStyle 可以得到样式的计算值。而 append元素到 documentFragment 中时,是不会计算元素的样式表,所以 documentFragment 性能更优。当然现在浏览器的优化已经做的很好了,

当 append元素到 document中后,没有访问 getComputedStyle 之类的方法时,现代浏览器也可以把样式表的计算推迟到脚本执行之后。

最后修改代码如下:

<ul id="container"></ul>

<script>
		//需要插入的容器
let ul =  document.getElementById('container');
// 插入十万条数据
let total = 100000;
// 一次插入 20 条
let once = 20;
//总页数
let page = total/once
//每条记录的索引
let index = 0;
//循环加载数据
function loop(curTotal,curIndex){
if(curTotal <= 0){
	return false;    
}   
//每页多少条
let pageCount = Math.min(curTotal, once);
    window.requestAnimationFrame(function(){
        let fragment =  document.createDocumentFragment();
		for(let i = 0; i< pageCount; i++){
		            let li = document.createElement('li');
		            li.innerText= curIndex + i+' : '+~~(Math.random()* total)
		            fragment.appendChild(li)        
		}
        ul.appendChild(fragment)
        loop(curTotal - pageCount,curIndex + pageCount)
})}

loop(total,index);
</script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值