性能优化之 -- 虚拟滚动

what

虚拟滚动是一种优化长列表性能的技术,它只渲染可见部分的列表项,而不是渲染整个列表。
本质上就是 按需渲染。

核心

  • 根据 可见区域高度 和 容器滚动位置,计算出需要渲染的列表项,而不渲染其他列表项。
  • 没有原生的滚动,通过js模拟的滚动:并非真实的滚动,而是重新计算元素的位置

好处

大大减少DOM操作,减少渲染时间和内存占用。从而解决页面加载慢、卡顿等问题,改善用户体验。

key-point

已知

容器高度 和 消息列表

需计算

  • 渲染的数据长度
  • 渲染的起始位置

计算时机

  • 监听滚轮时间,得到delaY,即滚轮的滚动量

举例

如直播间的聊天室

代码实现

注意,这里是伪代码,详细参考

class VirtualScroll {
	constructor({ el, list, itemElementGenerator, itemHeight }) {
		/**
		 * 1,入参赋值
		 */
		this.$list = el; // 视口元素
		this.list = list; // 需要展示的列表数据
		this.itemHeight = itemHeight; // 每个列表元素的高度
		this.itemElementGenerator = itemElementGenerator; // 列表元素的DOM生成器

		/**
		 * 2,初始化一些计算出的备用数据
		 */
		// 重新构造列表
		this.mapList();
		// 设置一些基础样式,计算 出容器高度和内容高度
		this.initContainer();

		/**
		 * 3,绑定监听函数
		 */
		this.bindEvents();
	}
	initContainer() {
		this.containerHeight = this.$list.clientHeight;
		this.$list.style.overflow = 'hidden';
		this.contentHeight = sumHeight(this._list);
	}
	mapList() {
		this._list = this.list.map((item, i) => ({
			height: this.itemHeight,
			index: i,
			item: item,
		}));
	}

	/**
	 * 4,监听scroll,计算滚动量,触发渲染
	 */
	handleScroll(e) {
		e.preventDefault();
		let offset = 0;
		const scrollSpace = this.contentHeight - this.containerHeight;
		offset += e.deltaY;
		offset = Math.min(y, scrollSpace);

		// 触发渲染
		this.render(offset);
	}

	/**
	 * 5,核心方法:render
	 */
	render(offset) {
    // 画图理解
		const headIndex = findIndexOverHeight(this._list, offset);
		const tailIndex = findIndexOverHeight(
			this._list,
			offset + this.containerHeight
		);

		// 上方半可见的元素的偏移量
		this.renderOffset = offset - sumHeight(this._list, 0, headIndex);

		this.renderList = this._list.slice(headIndex, tailIndex + 1);

    // 绘制,插入
		const $listWp = document.createElement('div');
		this.renderList.forEach((item) => {
			const $el = this.itemElementGenerator(item);
			$listWp.appendChild($el);
		});
		$listWp.style.transform = `translateY(-${this.renderOffset}px)`;
		this.$list.innerHTML = '';
		this.$list.appendChild($listWp);
	}

	bindEvents() {
		this.$list.addEventListener('wheel', this.handleScroll);
	}
}

// 找到第一个累加高度大于指定高度的序号,针对不定高度的item
export function findIndexOverHeight(list, offset) {
	let currentHeight = 0;
	for (let i = 0; i < list.length; i++) {
		const { height } = list[i];
		currentHeight += height;

		if (currentHeight > offset) {
			return i;
		}
	}

	return list.length - 1;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值