Я считаю, что каждый сталкивался с трудностями при рендеринге длинных списков или страниц. Длинные списки и страницы могут иметь большое влияние на скорость рендеринга первого экрана и вызывать некоторые негладкие ощущения при прокрутке страницы.
Недавно я также столкнулся с этой проблемой и обнаружил, что помимо непосредственного использования пейджинга, решение виртуальной прокрутки очень популярно, поэтому я также переделал колесо виртуальной прокрутки в vue. Виртуальная прокрутка просто означает рендеринг содержимого в пределах видимого в данный момент диапазона в браузере, динамическое вычисление отображаемого содержимого по положению пользователя, перемещающего полосу прокрутки, и заполнение остальных пробелами, чтобы дать пользователю иллюзию длинного списка.
Это колесо на самом деле не так сложно, как вы думаете.Ниже приводится подробное введение в то, как это колесо сделано.Если вы сталкиваетесь с одной и той же сценой в своей работе и учебе, вы также можете применить это решение. Ниже приведена простая демонстрация виртуальной прокрутки, которую я реализовал:онлайн демоиисходный код.
Структура дома
Базовая DOM-структура виртуальной прокрутки на самом деле представляет собой простой список, который можно описать следующим кодом в Vue.
<div style="overflow-y: scroll; height: 300px;" @scroll="handleScroll">
<div v-for="item in items" :key="`${item.id}`">
<slot :data="item">
</slot>
</div>
</div>
Здесь используется Vue.scoped slot
Для обработки содержимого пользовательского дома и входящих данных содержимого пользовательского дома, если нетscoped slot
, мы также можем сделать это, позволив пользователю определить конкретную функцию рендеринга во входящем объекте данных при передаче данных.
С помощью этой настраиваемой структуры списка dom и прокручиваемого контейнера фиксированной высоты снаружи мы реализовали базовый dom для всех спископодобных компонентов. Затем следующее, что нужно сделать, это заполнить высоту прокрутки за пределами видимого списка. Есть много способов сделать это, например, определить начало и конец списка.<div>
, путем изменения<div>
height для управления общей высотой, например, через контрольный списокpadding-top
иpadding-bottom
Для управления; другим примером является прямое задание высоты списка на сумму высот всех элементов путем определенияposition
включитьtop
Также возможно найти... Затем с помощью этой структуры dom мы можем вычислить отображаемый контент.
Прослушивание событий прокрутки для обновления содержимого списка (метод handleScroll)
1. Рассчитайте видимый диапазон списка
Прежде всего, мы можем быть уверены, что видимый диапазон списка представляет собой содержимое непрерывного массива, поэтому этот расчет упрощается, чтобы найти начальную и конечную точки содержимого непрерывного массива. Две части информации, от которых зависят начальная и конечная точки: одна — конкретная координата y каждого элемента в списке, а другая — начальная и конечная точки текущего видимого диапазона. Координата Y каждого элемента в списке может быть получена путем накопления высоты каждого элемента в цикле, чтобы получить координату Y каждого элемента, как показано ниже.
начальная точка текущего видимого диапазонаs
контейнер спискаscrollTop
атрибут и конечная точкаe
этоs
Плюс высота контейнера списка. Теперь у нас есть вся информация для расчета начальной и конечной точек массива.Вычисление начальной точки массива заключается в том, чтобы найти начальную точку, которая не больше видимого диапазона в y-координатах всех элементов.s
, вычисление конечной точки массива заключается в нахождении y-координаты всех элементов, которая не меньшеe
элементы, как показано на рисунке ниже
Когда каждый элемент массива имеет нефиксированную высоту, мы используем метод дихотомии (о конкретной реализации см.исходный код) для нахождения верхней и нижней границ массива; когда каждый элемент массива имеет фиксированную высоту, мы можем напрямую использоватьs
Разделите на высоту каждого элемента и округлите вниз (этаж), чтобы получить верхнюю границу, используйтеe
Разделите на высоту каждого округленного вверх элемента (ceil), чтобы получить нижнюю границу. затем используйтеslice
Метод получает окончательный массив элементов, которые необходимо отобразить.
Вы могли заметить, хотя мы использовали дихотомиюO(logN)
с прямым расчетомO(1)
вместо обычного обхода, чтобы найти верхнюю и нижнюю границы, ноslice
метод по-прежнему увеличивает общую сложность доO(N)
, так что эта оптимизация имеет определенный эффект ускорения только в случае конечных массивов. Итак, в практических приложениях мы столкнемся с довольно большим массивом, настолько большим, что его можно будет игнорировать.O(logN)
иO(1)
Что с улучшением? Ответ — нет, потому что нам сложно встретить такой массив в практических приложениях из-за ограничения памяти браузера на странице.
2. Рассчитайте высоту верхнего и нижнего отступов.
С верхней и нижней границами массива вычисление верхней и нижней высоты заполнения на самом деле очень интуитивно понятно.padding-top
иpadding-bottom
свойства в качестве примера, как показано на рисунке
режим страницы
Часто нам нужно оптимизировать не длинный список, а длинную страницу, так что же изменится в приведенном выше методе расчета?
Сначала нам нужно изменить начальную точку видимого диапазонаs
с конечной точкойe
Метод расчета , для страницы начальная точка видимого диапазона:window.pageYOffset || document.documentElement.scrollTop
; Конечная точка это начальная точка плюс высота видимого диапазона, здесь мы используем расчет высотыwindow.innerHeight || document.documentElement.clientHeight
, но обратите внимание, что эти два свойства возвращают разные значения, когда на странице есть полосы прокрутки,innerHeight
будет содержать высоту полосы прокрутки,clientHeight
Не включает высоту полосы прокрутки.
После расчета видимого диапазона нам также необходимо настроить координату y массива. Исходная y-координата массива относится к контейнеру прокрутки, теперь нам нужно настроить y-координату массива относительно страницы. Есть два метода корректировки: один — добавить контейнер прокрутки при вычислении координаты y.offsetTop
Атрибуты, второй — начальная точка расчета видимой дальности.s
с конечной точкойe
, вычтите контейнер прокруткиoffsetTop
Атрибуты.
После корректировки координат нам также нужно установить контейнер прокруткиheight
иoverflow-y
Атрибут удален, что позволяет контейнеру свободно расти, пока контейнер перекатывается.scroll
событие переданоwindow
объект, реализующий виртуальную прокрутку страницы. В постраничном режиме мы можем реализовать такие оптимизации для любой длинной страницы с блоками фиксированной высоты.
Больше мест для оптимизации
1. Оптимизация отображения прокрутки
Когда данные обновления прокручиваются слишком часто, рендеринг будет мерцать, и тогда нам нужно пройтиrequestAnimationFrame
Чтобы вызвать список обновлений, чтобы реализовать управление скоростью списка обновлений, чтобы создать сглаженную анимацию прокрутки.
2. Кэш списка
Vue помогает нам решить здесь некоторые проблемы с обновлением списка.Например, в мелкомасштабных изменениях массива, вызванных прокруткой, Vue будет повторно использовать ранее отрендеренные узлы для обновления списка. Если вы не используете аналогичный фреймворк, вам нужно самостоятельно обрабатывать эту часть логики повторного использования.
Кроме того, мы можем напрямую кэшировать содержимое рендеринга в пределах определенного диапазона. Например, мы можем ограничить количество узлов кеша и напрямую использовать узлы в кеше при обнаружении попадания в кеш во время прокрутки. узел кэша заполнен. В случае можно использовать определенную стратегию замены кэша, например замену наименее часто используемого (LFU) узла кэша новым узлом. Повторная оптимизация для мелкомасштабной прокрутки достигается за счет такого кеша списка.
3. Список вторичной переработки
Наша текущая практика по-прежнему заключается в постоянном уничтожении и воссоздании DOM во время прокрутки.Хотя накладные расходы на создание и уничтожение DOM каждый раз невелики, они все же занимают часть производительности браузера.
Когда каждый элемент в списке визуализируется с помощью унифицированного шаблона DOM или функции рендеринга, мы можем перерабатывать узлы DOM, которые находятся за пределами видимого диапазона, посредством повторного использования списка, а затем вводить новые данные в переработанный DOM. В узле, наконец, поместите переработанный node после обновления данных обратно в список, как показано на следующем рисунке. Метод повторного использования списка может гарантировать, что общее количество ваших узлов DOM находится в очень низком диапазоне, и сэкономить затраты на создание и уничтожение части DOM.
Наконец, спасибо за чтение. Если у вас есть какие-либо комментарии, предложения или проблемы, связанные с внешним интерфейсом, пожалуйста, не стесняйтесь общаться со мной. Это моеgithub, хорошего дня! :)
Ссылки по теме
Компонент виртуальной прокрутки Vue с учетом прокрутки по оси X
Добавлен компонент виртуальной прокрутки vue для кеша списка и повторного использования списка.