Решение для оптимизации длинного списка React: реактивно-виртуализированное

внешний интерфейс GitHub React.js
Решение для оптимизации длинного списка React: реактивно-виртуализированное

github

react-virtualized — это отзывчивый компонент для эффективного рендеринга больших списков и табличных данных.

Типичные проблемы разработки

Если показано, в каждом классе есть классы 1/2/3 с более чем 1000 учеников.

Студенческие компоненты:

function Student({student}) {
    return <div>{student.name}</div>
}

Если мы отобразим весь список напрямую, только список студентов сгенерирует более 1000 тегов div.

Часто нашими студенческими компонентами будут:

function Student({student, ...rest}) {
    return (
        <div>
            ...
                <div>{student.name} ....</div>
            ...
        </div>
    )
}

Количество DOM в это время становится невообразимым.

Все мы знаем, что если структура DOM слишком велика, у пользователя возникнут проблемы с взаимодействием с веб-страницей, такие как прокрутка, щелчок и другие распространенные операции. отражение виртуального DOM в реальном DOM также будет отличным.. Когда пользователь нажимает, чтобы переключить классы, происходит зависание второго уровня.

Оптимизирован с реактивной виртуализацией

В экосистеме react react-virtualized уже давно существует как оптимизация длинного списка, сообщество его обновляет и поддерживает, а обсуждения идут постоянно, что также означает, что это давняя острая проблема!

Основная идея решения вышеуказанных проблем: загружать только компоненты в видимой области

react-virtualized различает нашу сцену прокрутки на локальную прокрутку в области просмотра и прокрутку на основе области просмотра Первая эквивалентна открытию независимой области прокрутки на странице, которая принадлежит внутренней прокрутке, которая очень похожа на прокрутку с помощью iscroll, а затем Другие используют прокрутку как часть прокрутки окна (для мобильных это более распространено), исходя из этого рассчитывают компоненты, которые нужно отображать в данный момент.

Выполнение

Компонент студента изменен на:

function Student({student, style, ...rest}) {
    return (
        <div style={style}>
            ...
                <div>{student.name} ....</div>
            ...
        </div>
    )
}

Компонент списка учащихся:

import React from 'react'
import { AutoSizer } from 'react-virtualized/dist/commonjs/AutoSizer'
import { List as VList } from 'react-virtualized/dist/commonjs/List'

class StudentList extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            list: []
        }
    }
    getList = () => {
        api.getList.then(res => {
            this.setState({
                list: res
            })
        })
    }
    componentDidMount() {
        this.getList()
    }
    render() {
        const { list } = this.state  
        const renderItem = ({ index, key, style }) => {
            return <Student key={key} student={list[index]} style{style} />
        }
        return (
            <div style={{height: 1000}}>
                <AutoSizer>
                    {({ width, height }) => (
                        <VList
                            width={width}
                            height={height}
                            overscanRowCount={10}
                            rowCount={list.length}
                            rowHeight={100}
                            rowRenderer={renderItem}
                        />
                    )}
                </AutoSizer>
            </div>
        )
    }
}

(Высота во внешнем стиле div необязательна. Например, если ваша веб-страница представляет собой гибкий макет, вы можете использовать flex: 1, чтобы позволить реактивной виртуализации вычислить эту высоту)

В это время, если рост каждого ученика одинаков, проблема в основном решена!

Тем не менее, проблема возникает снова.Иногда у нашего студента будет неопределенный рост.Есть два способа решения проблемы.Рекомендуется решение с реактивно-виртуализированным компонентом CellMeasurer.

метод первый

Компонент списка учащихся изменен на:

import React from 'react'
import { AutoSizer } from 'react-virtualized/dist/commonjs/AutoSizer'
import { List as VList } from 'react-virtualized/dist/commonjs/List'
import { CellMeasurerCache, CellMeasurer } from 'react-virtualized/dist/commonjs/CellMeasurer'

class StudentList extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            list: []
        }
    }
    measureCache = new CellMeasurerCache({
        fixedWidth: true,
        minHeight: 58
    })
    getList = () => {
        api.getList.then(res => {
            this.setState({
                list: res
            })
        })
    }
    componentDidMount() {
        this.getList()
    }
    render() {
        const { list } = this.state  
        const renderItem = ({ index, key, parent, style }) => {
            return (
                <CellMeasurer cache={this.measureCache} columnIndex={0} key={key} parent={parent} rowIndex={index}>
                    <Student key={key} student={list[index]} />
                </CellMeasurer>
            )
        }
        return (
            <div style={{height: 1000}}>
                <AutoSizer>
                    {({ width, height }) => (
                        <VList
                            ref={ref => this.VList = ref}
                            width={width}
                            height={height}
                            overscanRowCount={10}
                            rowCount={list.length}
                            rowHeight={this.getRowHeight}
                            rowRenderer={renderItem}
                            deferredMeasurementCache={this.measureCache}
                            rowHeight={this.measureCache.rowHeight}
                        />
                    )}
                </AutoSizer>
            </div>
        )
    }
}

Способ второй

пройти черезreact-heightилиissueМетод, упомянутый в обратном вызове расчета, решается на примере react-height:

Компонент списка учащихся изменен на:

import React from 'react'
import { AutoSizer } from 'react-virtualized/dist/commonjs/AutoSizer'
import { List as VList } from 'react-virtualized/dist/commonjs/List'
import ReactHeight from 'react-height'

class StudentList extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            list: []
            heights = []
        }
    }
    getList = () => {
        api.getList.then(res => {
            this.setState({
                list: res
            })
        })
    }
    componentDidMount() {
        this.getList()
    }
    handleHeightReady = (height, index) => {
        const heights = [...this.state.heights]
        heights.push({
            index,
            height
        })
        this.setState({
            heights
        }, this.vList.recomputeRowHeights(index))
    }
    getRowHeight = ({ index }) => {
        const row = this.heights.find(item => item.index === index)
        return row ? row.height : 100
    }
    render() {
        const { list } = this.state  
        const renderItem = ({ index, key, style }) => {
            if (this.heights.find(item => item.index === index)) {
                return <Student key={key} student={list[index]} style{style} />
            }
            return (
                <div key={key} style={style}>
                    <ReactHeight
                        onHeightReady={height => {
                            this.handleHeightReady(height, index)
                        }}
                    >
                        <Student key={key} student={list[index]} />
                    </ReactHeight>
                </div>
            )
        }
        return (
            <div style={{height: 1000}}>
                <AutoSizer>
                    {({ width, height }) => (
                        <VList
                            ref={ref => this.VList = ref}
                            width={width}
                            height={height}
                            overscanRowCount={10}
                            rowCount={list.length}
                            rowHeight={this.getRowHeight}
                            rowRenderer={renderItem}
                        />
                    )}
                </AutoSizer>
            </div>
        )
    }
}

Теперь, если данные вашего списка получены сразу, проблема в основном решена!

А прокрутка загрузки?

реактивная виртуализация официально доступнаInfiniteLoader, надпись такая же, как и официальная!

Что, если отложить этот классический случай и разработать окно чата?

Окно чата отображается в обратном порядке. Когда данные загружаются в первый раз, положение полосы прокрутки должно быть внизу. Компонент List в react-virtualized предоставляет нам метод scrollToRow(index) для реализации. Если рост ученика непостоянен, используйте его напрямую.Кусок пирога, то есть вы не можете прокрутить вниз за один раз.Временное решение:

scrollToRow = (): void => {
    const rowIndex = this.props.list.length - 1
    this.vList.scrollToRow(rowIndex)
    clearTimeout(this.scrollToRowTimer)
    this.scrollToRowTimer = setTimeout(() => {
        if (this.vList) {
            this.vList.scrollToRow(rowIndex)
        }
    }, 10)
}

Вызывается при первой загрузке данных

Поскольку InfiniteLoader не поддерживает требование загрузки в обратном порядке, он может только получать данные прокрутки и выполнять связанные операции через метод onScroll.Следует отметить, что при возврате данных предыдущей страницы, если вы используете метод 1, вы необходимо выполнить this.measureCache.clear/clearAll, уведомить react-virtualized для пересчета.Метод 2 должен добавить все индексы в массиве state.heights к количеству данных на этот раз

getList = () => {
    api.getList.then(res => {
        const heights = [...this.state.heights]
        heights.map(item => {
            return {
                index: item.index + res.length,
                height: item.height
            }
        })
        this.setState({
            list: [...res, ...this.state.list],
            heights
        })
    })
}

react-virtualized также имеет много интересных функций, а его собственная реализация также имеет большое справочное значение!react-virtualized githubпрогуляться