网站漏洞怎么修复,wordpress 功能,tomcat 怎么做网站,受欢迎的免费建站虚拟列表 - Vue3实现一个可动态改变高度的虚拟滚动列表
前言
在开发中经常遇到大量的渲染列表数据问题#xff0c;往往我们就只是简单地遍历渲染#xff0c;没有过多地去关注是否会存在性能问题#xff0c;这导致如果数据量较大的时候#xff0c;比如上万条数据#xff…虚拟列表 - Vue3实现一个可动态改变高度的虚拟滚动列表
前言
在开发中经常遇到大量的渲染列表数据问题往往我们就只是简单地遍历渲染没有过多地去关注是否会存在性能问题这导致如果数据量较大的时候比如上万条数据将会在dom中渲染上万个节点这将加大浏览器的开销可能会导致页面卡顿加载慢等性能问题。因此在渲染大量数据时可以选择使用虚拟列表只渲染用户可视区域内的dom节点。该组件已开源上传npm可以直接安装使用Git地址在文尾。
虚拟列表实现原理
每条固定高度
1、通过传入组件的每条数据的高度计算整个列表的高度从而得到滚动列表的总高并将总高赋值给列表。 2、监听滚动事件监听外层容器的滚动事件并确定可视区域内起止数据在总数据的索引值这可以通过scrollTop来实现。 3、设置数据对应的元素为每条数据设置一个绝对定位其中top等于索引值乘以每条数据的高度。 4、考虑缓冲条数为了避免滑动过快产生空白可以设置缓冲条数。具体来说如果滚动到底部可以只显示最后N条数据如果滚动到上部可以只显示前N条数据。 这样就可以实现一个固定高度的虚拟列表。
每条动态高度
原理和固定高度基本一致差别在于用户可以预先定义每条数据的高度在渲染时再动态获取每一条数据的实际高度从而重新计算滚动列表的总体高度。
主要代码实现
模板部分
showItemList循环可视区域内的数据缓存区的数据
templatediv classvirtual-wrap refvirtualWrap :style{width: width px,height: height px,} scrollscrollHandlediv classvirtual-content :style{height: totalEstimatedHeight px}list-item v-for(item,index) in showItemList :keyitem.dataIndexindex :indexitem.dataIndex :dataitem.data :styleitem.styleonSizeChangesizeChangeHandletemplate #slot-scopeslotPropsslot nameslot-scope :slotPropsslotProps/slot/template/list-item/div/div
/template获取需要渲染的数据
通过可视区域内的开始和结束索引获取需要渲染的列表数据。
const getCurrentChildren () {//重新计算高度estimatedHeight(props.itemEstimatedSize,props.itemCount)const [startIndex, endIndex] getRangeToRender(props, scrollOffset.value)const items [];for (let i startIndex; i endIndex; i) {const item getItemMetaData(i);const itemStyle {position: absolute,height: item.size px,width: 100%,top: item.offset px,};items.push({style: itemStyle,data: props.data[i],dataIndex:i});}showItemList.value items;
}获取开始和结束索引
const getRangeToRender (props: any, scrollOffset: any) {const { itemCount } props;const startIndex getStartIndex(props, scrollOffset);const endIndex getEndIndex(props, startIndex props.buffCount);return [Math.max(0, startIndex -1),Math.min(itemCount - 1, endIndex ),];
};const getStartIndex (props: any, scrollOffset: number) {const { itemCount } props;let index 0;while (true) {const currentOffset getItemMetaData(index).offset;if (currentOffset scrollOffset) return index;if (index itemCount) return itemCount;index}
}const getEndIndex (props: any, startIndex: number) {const { height, itemCount } props;// 获取可视区内开始的项const startItem getItemMetaData(startIndex);// 可视区内最大的offset值const maxOffset Number(startItem.offset) Number(height);// 开始项的下一项的offset之后不断累加此offset知道等于或超过最大offset就是找到结束索引了let offset Number(startItem.offset) startItem.size;// 结束索引let endIndex startIndex;// 累加offsetwhile (offset maxOffset endIndex (itemCount - 1)) {endIndex;const currentItem getItemMetaData(endIndex);offset currentItem.size;}// 更新已计算的项的索引值measuredData.lastMeasuredItemIndex endIndex;return endIndex;
};动态计算节点高度 const estimatedHeight (defaultEstimatedItemSize 50, itemCount: number) {let measuredHeight 0;const { measuredDataMap, lastMeasuredItemIndex } measuredData;// 计算已经获取过真实高度的项的高度之和if (lastMeasuredItemIndex 0) {const lastMeasuredItem measuredDataMap[lastMeasuredItemIndex];measuredHeight lastMeasuredItem.offset lastMeasuredItem.size;}// 未计算过真实高度的项数const unMeasuredItemsCount itemCount - measuredData.lastMeasuredItemIndex - 1;// 预测总高度totalEstimatedHeight.value measuredHeight unMeasuredItemsCount * defaultEstimatedItemSize;
}子组件实现
1、通过ResizeObserver在子节点高度变化时触发父组件的方法重新计算整体高度。 2、通过插槽将每条数据动态插入到列表中。
templatediv :stylestyle refdomRefslot nameslot-scope :datadata/slot/div
/template
script langts setup
import { ref, onMounted, onUnmounted } from vueconst emit defineEmits([onSizeChange]);const props defineProps({style: {type: Object,default: () { }},data: {type: Object,default: () { }},index: {type: Number,default: 0}
})const domRef refany(null);
const resizeObserver:any null;onMounted(() {const domNode domRef.value.children[0];emit(onSizeChange, props.index, domNode);const resizeObserver new ResizeObserver(() {emit(onSizeChange, props.index, domNode);});resizeObserver.observe(domNode);
})onUnmounted(() {if (resizeObserver) {resizeObserver?.unobserve(domRef.value.children[0]);}
})
/script组件使用
npm install fcli/vue-virtually-list --save-dev 来安装在项目中使用
import VueVirtuallyList from fcli/vue-virtually-list;
const appcreateApp(App)
app.use(VueVirtuallyList);
示例 div classcontentvue-virtually-list :datalist :height400 :width600 :itemCount1000 :itemEstimatedSize20 :buffCount50template #slot-scope{slotProps}div classli{{ slotProps.data.text }}/div/template/vue-virtually-list
/div
属性属性名称类型可选值data列表数据Array[]height虚拟容器的高度number0width虚拟容器的宽度number0itemCount滚动列表的条数number0itemEstimatedSize预设每行数据的高度number可不填组件会动态计算buffCount上下缓冲区的条数number增加快速滚动时的流畅性#slot-scope插槽 object slotProps.data
slot
例 template #slot-scope{slotProps}div classli{{ slotProps.data.text }}/div/templateGit地址https://gitee.com/fcli/vue-virtually-list.git