虚拟列表的渐进式实现(vue,react)

vue

1、地址(vue)

2、react (下面是几个版本 样式在最后面)

/*

* @Author: your name

* @Date: 2020-03-13 11:11:23

* @LastEditTime: 2020-05-13 16:39:01

* @LastEditors: Please set LastEditors

* @Description: 列表项等宽

* @FilePath: /optimus/src/pages/test/Index/hooks.js

*/

import React, { useState, useEffect, useRef } from "react";

import styles from "./style.less";

const arr = [];

for (let index = 0; index < 200; index++) {

arr.push({

index,

height: parseInt(Math.random() * 30 + 20)

});

}

const Index = (props) => {

const [height, setHeight] = useState(0);

const [itemHeight] = useState(30);

const [totalList] = useState(arr);

const [list, setList] = useState([]);

const couterRef = useRef();

const totalRef = useRef();

useEffect(() => {

setHeight(totalList.length * itemHeight);

}, [totalList]);

useEffect(() => {

updateVisibleData();

}, [totalList]);

const onScrollCapture = (view) => {

const scrollTop = totalRef.current.scrollTop;

updateVisibleData(scrollTop);

};

const updateVisibleData = (scrollTop) => {

scrollTop = scrollTop || 0;

const clientHeight = totalRef.current.clientHeight;

const visibleCount = Math.ceil(clientHeight / itemHeight);

const start = Math.floor(scrollTop/ itemHeight);

const end = start + visibleCount;

const _list = totalList.slice(start, end);

setList(_list);

couterRef.current.style.webkitTransform = `translate3d(0, ${start * itemHeight}px, 0)`;

};

return (

<div className={styles.wrap}>

<div

className={styles.listView}

ref={totalRef}

onScrollCapture={onScrollCapture}

>

<div

className={styles.listViewPhantom}

style={{ height: height }}

></div>

<div className={styles.listViewContent} ref={couterRef}>

{list.map((item, index) => {

return (

<div

className={styles.listViewItem}

style={{ height: itemHeight }}

key={index}

>

{item.index}

</div>

);

})}

</div>

</div>

</div>

);

};

export default Index;

/*

* @Author: x

* @Date: 2020-03-13 11:11:23

* @LastEditTime: 2020-05-13 16:34:47

* @LastEditors: Please set LastEditors

* @Description: react 列表项不等宽

* @FilePath: /optimus/src/pages/test/Index/hooks.js

*/

import React, { useState, useEffect, useRef } from "react";

import styles from "./style.less";

const arr = [];

for (let index = 0; index < 200; index++) {

arr.push({

index,

height: parseInt(Math.random() * 30 + 20)

});

}

const Index = (props) => {

const [height, setHeight] = useState(0);

const [totalList, setTotalList] = useState(arr);

const [list, setList] = useState([]);

const couterRef = useRef();

const totalRef = useRef();

useEffect(() => {

let total = 0;

let index = 0;

let length = totalList.length;

for (index; index < length; index++) {

total += totalList[index].height;

}

setHeight(total);

}, [totalList]);

useEffect(() => {

updateVisibleData();

}, [totalList]);

const onScrollCapture = (view) => {

const scrollTop = totalRef.current.scrollTop;

updateVisibleData(scrollTop);

};

const updateVisibleData = (scrollTop) => {

scrollTop = scrollTop || 0;

const start = findNearestItemIndex(scrollTop);

const end = findNearestItemIndex(scrollTop + totalRef.current.clientHeight);

const _list = totalList.slice(start, Math.min(end + 1, totalList.length));

setList(_list);

couterRef.current.style.webkitTransform = `translate3d(0, ${getItemSizeAndOffset(start).offset}px, 0)`;

};

const findNearestItemIndex = scrollTop => {

let total = 0;

for (let i = 0, j = totalList.length; i < j; i++) {

const size = totalList[i].height;

total += size;

if (total >= scrollTop || i === j -1) {

return i;

}

}

return 0;

}

const getItemSizeAndOffset = start => {

let total = 0;

for (let i = 0, j = Math.min(start, totalList.length - 1); i <= j; i++) {

const size = totalList[i].height;

if (i === j) {

return {

offset: total,

size

};

}

total += size;

}

return {

offset: 0,

size: 0

};

}

return (

<div className={styles.wrap}>

<div

className={styles.listView}

ref={totalRef}

onScrollCapture={onScrollCapture}

>

<div

className={styles.listViewPhantom}

style={{ height: height }}

></div>

<div className={styles.listViewContent} ref={couterRef}>

{list.map((item, index) => {

return (

<div

className={styles.listViewItem}

style={{ height: item.height }}

key={index}

>

{item.index}

</div>

);

})}

</div>

</div>

</div>

);

};

export default Index;

.wrap{

width: 100%;

height: 800px;

display: flex;

justify-content: center;

align-items: center;

background-color: antiquewhite;

}

.listView {

height: 400px;

width: 160px;

overflow: auto;

position: relative;

border: 1px solid #aaa;

}

.listViewPhantom {

position: absolute;

left: 0;

top: 0;

right: 0;

z-index: 100;

width: 160px;

background-color: rgba(red,0.5);

}

.listViewContent {

left: 0;

right: 0;

top: 0;

position: absolute;

}

.listViewItem {

padding: 5px;

color: #666;

box-sizing: border-box;

border-bottom: 1px solid red;

display: flex;

align-items: center;

}

写在最后

  题目当然还可以再优化:

    对itemHeight的缓存;

    对contextHeight的高度计算;

    对缓存结果的算法查询;

    对未缓存结果的算法查询;

    根据渲染结果动态更新列表项的高度;

    数据源更新时尽量范围小的删除失效缓存;  

    。。。

  优化之路永无尽头;

以上是 虚拟列表的渐进式实现(vue,react) 的全部内容, 来源链接: utcz.com/z/380678.html

回到顶部