对于数量大的列表中,如果一下子要全部渲染,都会出现渲染慢的问题,导致整个页面卡顿。因此针对这种情况,列举下常见方案。
1、分页加载
传统大数据列表处理方式,通过分页调用一部分数据,减少大量数据渲染
2、懒加载(滚动加载)
原理:通过监听滚动事件和回调函数的机制,配合使用元素相关属性和方法scrollTop、getClientHeight(container)、scrollHeight
实现了滚动加载数据的功能。之前在赛事项目中就是单纯使用这种技术加载列表数据
简单案例:
//InfiniteScrollUtil.js
const throttle = (fn, wait) => {
let inThrottle, lastFn, lastTime;
return function () {
const context = this;
const args = arguments;
if (!inThrottle) {
fn.apply(context, args);
lastTime = Date.now();
inThrottle = true;
} else {
clearTimeout(lastFn);
lastFn = setTimeout(function () {
if (Date.now() - lastTime >= wait) {
fn.apply(context, args);
lastTime = Date.now();
}
}, Math.max(wait - (Date.now() - lastTime), 0));
}
};
};
const getPositionSize = (el, prop) => {
return el === window || el === document
? document.documentElement[prop]
: el[prop];
};
const getClientHeight = (el) => {
return getPositionSize(el, "clientHeight");
};
const isFunction = (functionToCheck) => {
const getType = {};
return (
functionToCheck &&
getType.toString.call(functionToCheck) === "[object AsyncFunction]"
);
};
export const handleScroll = function (container, cb, options) {
const { distance } = options;
const containerInfo = container.getBoundingClientRect();
if (!containerInfo.width && !containerInfo.height) return;
let shouldTrigger = false;
const scrollBottom = container.scrollTop + getClientHeight(container);
shouldTrigger = container.scrollHeight - scrollBottom <= distance;
if (shouldTrigger && isFunction(cb)) {
cb.call(container, container, options);
}
};
export const onScroll = (container, options) => {
return throttle(
handleScroll.bind(container, container, options.cbFunc, options),
options.delay
);
};
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
</style>
<script type="module">
import { onScroll } from "./InfiniteScrollUtil.js";
const createLiEle=(container)=>{
const fragment = document.createDocumentFragment();
new Array(10).fill(1).forEach(
(m,i)=>{
const tempEle = document.createElement("li");
tempEle.innerHTML =`${document.querySelectorAll("li").length+i+1}数据`
fragment.append(tempEle);
}
)
container.append(fragment);
}
window.onload=()=>{
const container = document.querySelector("ul");
const options = {
delay: 200,
distance: 10,
cbFunc: async (container, options) => {
createLiEle(container);
}
};
if (container) {
const onScrollFunc = onScroll(container, options);
container.addEventListener("scroll", onScrollFunc);
}
createLiEle(container);
}
</script>
</head>
<body>
<ul style="height: 100px;overflow-y: scroll;">
</ul>
</body>
</html>
3、虚拟列表
原理:它通过只渲染可见区域内的数据项(根据可见区域的大小和滚动位置,动态计算需要渲染的数据项,同时设置padding,并在滚动时进行相应的更新),而不是渲染整个列表,从而提高了性能和响应速度。
简单案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
margin: 0;
padding: 0;
}
#scroller{
height: 100px;overflow-y: scroll;
}
#container{
height: 100px;
}
.item{
height: 20px;
text-align: center;
}
</style>
</head>
<body>
<div id="scroller">
<div id="container">
</div>
</div>
</body>
</html>
<script>
const scroller=document.querySelector("#scroller")
const container=document.querySelector("#container")
const sourceData=new Array(100000).fill(1).map((m,i)=>i);
const itemHeight=20;
const containerHeight=container.clientHeight;
let [startIndex,ednIndex,paddingTop,paddingBottom]=[0,9,0,sourceData.length*itemHeight-100]
const createLiStr=(startIndex,ednIndex)=>{
const data=sourceData.slice(startIndex,ednIndex+1);
return data.map(m=>`<div class="item">${m}</div>`).join("");
}
const setPadding=(paddingTop,paddingBottom)=>{
container.setAttribute("style",`padding-top:${paddingTop}px;padding-bottom:${paddingBottom}px`)
}
scroller.addEventListener("scroll",()=>{
startIndex=Math.floor(scroller.scrollTop/itemHeight);
ednIndex=Math.floor((scroller.scrollTop+containerHeight)/itemHeight);
container.innerHTML= createLiStr(startIndex,ednIndex);
paddingTop=itemHeight*startIndex;
paddingBottom=sourceData.length*itemHeight - 100 -paddingTop
setPadding(paddingTop,paddingBottom)
})
container.innerHTML= createLiStr(startIndex,ednIndex);
setPadding(paddingTop,paddingBottom)
</script>
常见框架中使用虚拟技术的插件
React 相关插件和库:
- react-virtualized:React Virtualized 是一个用于构建高性能虚拟列表的 React 组件库。它提供了一系列的组件,如 VirtualList、InfiniteLoader、AutoSizer 等,用于实现可滚动的、大量数据的列表。React Virtualized 提供了丰富的配置选项和 API,使开发者能够高度自定义虚拟列表的行为。
- react-window:React Window 是另一个用于构建高效虚拟列表的 React 组件库。它提供了一系列的组件,如 FixedSizeList、VariableSizeList、FixedSizeGrid 等,可以根据不同的需求选择适当的组件。React Window 的组件具有高性能和低内存占用的特点,适用于大数据集的列表展示。
Vue 相关插件和库:
- vue-virtual-scroller:Vue Virtual Scroller 是一个用于 Vue.js 的虚拟列表组件库。它提供了一系列的组件,如 VirtualScroller、DynamicScroller 等,用于实现高性能的虚拟滚动列表。Vue Virtual Scroller 提供了可配置的选项和事件钩子,方便自定义和扩展。
- vue-virtual-scroll-list:Vue Virtual Scroll List 是另一个用于 Vue.js 的虚拟列表插件。它基于虚拟滚动技术,只渲染可见区域内的数据项,从而提高性能和响应速度。Vue Virtual Scroll List 提供了简单易用的 API,支持动态高度、滚动到指定位置、无限加载等功能。
参考
© 版权声明
文章版权归作者所有,未经允许请勿转载,侵权请联系 admin@trc20.tw 删除。
THE END