一次性在dom添加多个节点会产生很大的性能问题, 一次性加载多个dom元素的时候 页面会出现首次加载缓慢或者浏览器直接崩溃掉,介绍两种方法去优化
1、分时函数
<script type="text/javascript">
function createDom(data) {
const div = document.createElement('div');
div.innerHTML = data;
document.body.appendChild(div);
}
function chunkData (data, num, callback) {
let time;
const start = function() {
for (let i = 0; i < Math.min(num, data.length); i++) {
callback(data.shift());
}
}
time = setInterval(function () {
if(data.length === 0) {
clearInterval(time);
}
start();
} , 300);
}
window.onload = function () {
const data = [];//要加载的数据
for(var i = 0; i <= 10000; i++) {
data.push(i);
}
for(let i = 0; i < data.length; i++) { //1.简单粗暴的方法
createDom(i);
}
// chunkData(data, 20, createDom);//2.分时加载
}
</script>
下图一次性加载多个dom导致页面加载时间变长
采用分时加载后(使用chunkData方法)
判断渲染速度是否加快可以判断当发生 DOMContentLoaded 事件后,就会生成渲染树,生成渲染树就可以进行渲染了,这一过程更大程度上和硬件有关系了。
2、虚拟列表
虚拟列表的实现原理就是只渲染固定屏幕高度的几条数据 每次改变dom元素内容 模拟滚动 尽量不要去用插入dom并且删除dom这样的方式去优化 插入删除dom会造成浏览器的开销 引起浏览器回流
-
visibleCount
为计算可视区域的渲染条数 -
listHeight
为列表的总高度 用于撑开整个可视区域 用于滑动 -
startIndex
和endIndex
用于截取渲染数据
componentDidMount() {
const { data } = this.state; // 获取的全部数据
this.startIndex = 0
this.listHeight = data.length * this.itemSize;
this.visibleCount = Math.ceil(500 / this.itemSize) // 500是可视区的高度
this.endIndex = this.visibleCount
this.setState({ visibleData: data.slice(0,this.visibleCount) });
}
滚动监听事件
this.startOffset
的作用是用于定位可视区域的数据,因为滑动会将可视区域的列表向上滑动 所以我们要计算整块的高度 例如第2个元素要替换第1个元素的时刻 我们将列表向下定位一个块的高度 这时候第一个元素就正好显示在屏幕的第一个位置 否则第一个元素就会滑上去了 scrolltop减去this.scrollTop % this.itemSize是因为我们要制造滑动效果 不能在第一个元素滑上去的距离小于一整块高度的时候去调整他的top 这样就不会有滑动效果了
.wrapper {
margin-top: 100px;
border: 1px solid red;
height: 500px;
overflow: scroll;
position: relative;
-webkit-overflow-scrolling: touch;
}
.item {
height: 50px;
border: 1px solid darkcyan;
}
.phantom {
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.list {
left: 0;
right: 0;
top: 0;
position: absolute;
text-align: center;
}
onScrollList = (val) => {
const { data } = this.state;
this.startIndex = Math.floor(this.refs.lala.scrollTop / this.itemSize);
this.endIndex = this.startIndex + this.visibleCount;
this.scrollTop = this.refs.lala.scrollTop;
this.startOffset = this.scrollTop - (this.scrollTop % this.itemSize);
this.setState({ visibleData: data.slice(this.startIndex, Math.min(this.endIndex, data.length)) });
}
render () {
const { visibleData } = this.state;
return (
<div className='wrapper' ref={'lala'} onScroll={this.onScrollList}>
<div className='phantom' style={{ height: this.listHeight + 'px' }}></div>
<div className='list' style={{ top: this.startOffset + 'px' }}>
{
visibleData.map((item) => <div className='item'>{item}</div>)
}
</div>
</div>
)
}