задний план
В последние несколько месяцев я работал над системой сбора логов, так как эта система является унаследованным проектом, я также хочу внести в систему некоторые знаковые изменения и передать ее как эстафетную палочку.
Эта система ведения журнала внесла множество изменений от внешнего интерфейса до серверной части, таких как виртуальные списки, горячие обновления электронов, оптимизация SQL и добавление концепции пользователей, то есть необходимость входа в систему, обновления базу данных в Elasticsearch и так далее. Я поделюсь некоторыми вещами, которые я нахожу более интересными позже.
Первое, что я изменил, это отображение списка журналов. Этот список является относительно обычным узлом кучи, и страница будет приводить к увеличению количества узлов DOM по мере увеличения времени, что является проблемой, которую необходимо изменить.
Анализ требований
Прежде всего, поскольку коллеги уже давно привыкли к этому списку, лучше не вносить серьезных изменений в опыт. Для этого мне нужно перечислить предыдущие приближенные функции:
- Каждый раз, когда в списке появляется новое сообщение,можно увидеть последние данные.
- когда пользователь нажимаетКогда один из данных в списке, список должен перестать обновляться, т.е.остановить прокрутку.
- Когда список заблокирован,Когда полоса прокрутки прокручивается вниз, список автоматически прокручивается..
- Когда в списке есть данные в выбранном состоянии, вы можетеИспользуйте клавиши вверх, вниз, влево и вправо для перемещения фокуса.
После суммирования предыдущих функций мне нужно снова разобраться в своих потребностях:
- Список будет пополняться со временем, требуяУправляет количеством отображаемых узлов.
- Длина списка увеличивается при связи через WebSocket,Частота обновления данных слишком высока, и требуется буферный пул..
- Добавьте подсказку блокировки списка, вы можетеРазблокировать список вручную.
- мобильный фокусКогда подробности журнала отображаются сразу, из-за слишком высокой скорости движения необходимоУвеличить защиту от сотрясений.
Причесавшись, подумав, решила использоватьФиктивный список для замены существующего длинного списка, что также является началом пути к наступлению на яму.
начать разработку
Длинный список в виртуальный список
Может быть, все слышали об этой концепции или даже разработали виртуальный список раньше, мне все равно. Прежде чем мы начнем, позвольте мне дать вам немного концепции. (Стук по доске!!!)
Зачем нужен виртуальный список
Мы знаем, что когда браузер отображает страницу, чем больше количество узлов DOM, тем больше влияние на производительность каждый раз, когда она перерисовывается.
Предположим, нам нужно отобразить большой объем информации, порядка сотен тысяч единиц данных. В такой ситуации решений на самом деле много.Наше самое распространенное решение похоже на следующую страницу и предыдущую страницу на ПК, но это решение на самом деле не является дружественным с точки зрения опыта. Большинство пользователей предпочитают продолжать прокручивать вниз, чтобы увидеть новый контент, но это столкнется с проблемой, постоянной загрузкой данных, в результате чего на странице накапливается все больше и больше узлов, которые потребляют память. Она продолжает увеличиваться, и, наконец, даже прокрутка зависнет.
В это время, если мы повторно проанализируем его, мы обнаружим, что на самом деле есть много данных, которые нам не нужно видеть в большинстве случаев.Если мы рассмотрим только данные, которые мы можем видеть, объем данных, которые нам нужны для рендеринга на самом деле будет очень мало Очень хорошо Повышена эффективность рендеринга, уменьшены ненужные эффекты из-за большого количества перерисовок.
Разобравшись таким образом, почти готов появиться ответ — виртуальный список.
Что такое виртуальный список
На самом деле, нет ничего особенного в виртуальном списке. Чтобы положить его прямо, это способ отображения списка.Создайте контейнер на странице в качестве области просмотра, чтобы отобразить часть длинного списка в этой области просмотра., который отображает список в видимой области.
Как показано на рисунке, это простая модель виртуального списка.На рисунке есть несколько концепций, которые вам нужно немного понять:
- Зона просмотра.
- реальный список.
- начальный индекс.
- конециндекс.
видимая область
Вы можете понять визуальную область, как это, теперь у нас есть<div class="show-box">, чтобы добавить некоторые стили к этому элементу.
.show-box{
width: 375px;
height: 500px;
margin: 0 auto;
position: relative;
}
Благодаря этому стилю мы можем видеть, что высота контейнера визуальной области равна500px.
реальный список
Настоящий список — это список, который будет отображаться, поэтому его может быть трудно понять, например: количество списков, которые необходимо отобразить сейчас, составляет всего1000бар, но на самом деле количество списков, которые нужно отобразить на странице (данные, которые нужно увидеть), нужно только100статья, это100Бар — это то, что называется истинным списком.
<div class="list-body-box" @scroll="listScroll"> ----- 真实列表
<div class="list-body"> ------ 载体
</div>
</div>
-------------------------- style --------------------------------
.list-body {
min-height: 10px;
position: absolute;
width: 100%;
}
Здесь рекомендуетсяДлина фактического списка должна быть больше, чем высота видимой области., если есть полоса прокрутки, то можно пройтиscrollМонитор для выполнения некоторых других операций.
Возможно, в какой-то момент мне нужно будет объяснить вам, почему мой<div class="list-body">даабсолютное позиционирование.
Когда один из ваших элементов часто меняется, он лучше всего подходит для этого модуля путем абсолютного позиционирования, из потока документов вы можете уменьшить удар, вызванный с обратным холодильником.
Давайте сначала посмотрим на механизм рендеринга браузера
- Разобрать HTML, создать дерево DOM, разобрать CSS, создать дерево CSSOM
- Объедините дерево DOM и дерево CSSOM для создания дерева рендеринга.
- Макет (перекомпоновка): согласно сгенерированному дереву рендеринга выполните перекомпоновку (макет), чтобы получить геометрическую информацию (положение, размер) узла.
- Покраска (перерисовка): По геометрической информации, полученной из дерева рендеринга и перекомпоновки, получаются абсолютные пиксели узла
- Отображение: отправка пикселей на графический процессор для отображения на странице.
Абсолютное позиционирование или плавающее положение выходит за рамки обычного потока документов, что эквивалентно простому сохранению токена на узле, а затем использованию этого токена для сопоставления, поэтому, если вы используете метод абсолютного позиционирования, будет перерисован только этот элемент.
startIndex
Как упоминалось ранее, реальный список на самом деле является лишь небольшой частью общего списка, и существует гораздо больше списков, которые необходимо отобразить. Следовательно, реальный список можно рассматривать как фрагмент. первого элемента для рендерингаindex— позиция первого элемента фрагмента в общем списке, то есть в массивеindex.
Например: мой общий список (массив) имеет длину1000, а фрагмент списка, который необходимо отобразить,100—200, то начальная позиция, которая является массивомindexтогда99.
edIndex
Как объяснялось выше, последний элементindexда199.
Реализация виртуального списка
Здесь следует упомянуть, что мой фреймворк использует vue, поэтому реализация виртуальных списков также более удобна.
<div class="list-body-box">
<div class="list-body">
<templete v-for="(item, idx) in list" >
<div
v-if="idx >= startIdx && idx <= endIdx"
:key="idx"
class="list-row">
<div class="col-item col-1">{{item.col_1}}</div>
<div class="col-item col-2">{{item.col_2}}</div>
<div class="col-item col-3">{{item.col_3}}</div>
<div class="col-item col-4">{{item.col_4}}</div>
<div class="col-item col-5">{{item.col_5}}</div>
<div class="col-item col-6">{{item.col_6}}</div>
<div class="col-item col-7">{{item.col_7}}</div>
</div>
</templete>
</div>
</div>
На шаблоне особо ничего особо нет, в основном черезv-ifЧтобы управлять отображением списка, передайтеstartIdxа такжеendIdxувеличивать или уменьшать, чтобы отображать данные в разных местах,Увеличение этих двух значений включает прокрутку списка.
Ниже мы поговорим о реализации в коде автоматической прокрутки, в основном через активное событие для частого срабатывания пар.startIdxа такжеendIdxУвеличение или уменьшение.
let time = null;
...
autoScroll(){
time = setTimeout(()=>{
let listLen = this.list.length - 1;
this.endIdx = listLen;
this.startIdx = (listLen + 1) <= 100 ? 0 : listLen - 100;
this.autoScroll();
},300);
}
Как показано в коде, мне просто нужно, чтобы метод вызывал способ.autoScroll(), этот метод будетsetTimeoutПод действием самовызова,startIdxа такжеendIdxСписок будет постоянно увеличиваться для автоматической прокрутки, а сбоку есть выражение
this.startIdx = (listLen + 1) <= 100 ? 0 : listLen - 100;
Этот раздел в основном предназначен для решения, когда страница только что открыта или список пуст, длина списка на самом деле относительно короткая, и нет необходимости прокручивать, другими словами,startIndexОбщая длина списка должна быть0.
На этом этапе реализуется простой виртуальный список.
Буферный пул WebSocket
Мы используем WebSocket для передачи данных, и объем данных довольно велик. Поэтому велика вероятность, что данные будут обновляться слишком часто.После обновления данных изменится и страница, что окажет определенное влияние на производительность. Поэтому нам нужно контролировать эту частоту. Текущее решение состоит в том, чтобы добавить буферный пул.
Идея буферного пула, вероятно, такова: когда WebSocket передает данные, мы сначала сохраняем данные в течение этого периода в массиве, а затем проталкиваем данные в полный список через равные промежутки времени, например 500 мс, это схема Дросселирование может быть задействовано.
let socketPool = []; //存储一段时间的数据
let socketTimer;
socketFun( (data) => {
//先制造一个缓存区间,用来做缓存socket的数据
socketPool.push(data);
//每次都把当前的数据进行push到list
if(!socketTimer){
socketTimer = setTimeout(()=>{
this.appendRecord(socketPool);
socketPool.length = 0;
this.scrollToBottom();
socketTimer = null;
},500);
}
});
здесь сторонаappendRecord()это метод, используемый для обработки данных и помещения данных в список, иscrollToBottom()Это делается для того, чтобы при проталкивании данных в список список мог напрямую отображать последние данные, то есть прокручивать страницу до конца списка.
Буферный пул на самом деле является решением для повышения производительности.Суть этой программыто естьСокращение времени рендеринга страницы. Понять это можно так: может быть штук 10 данных, которые нужно рендерить каждую секунду.Если я каждый раз буду рендерить честно, то я буду рендерить 10 раз за 10 секунд, что на самом деле не нужно, так что мы можем рассмотреть рендеринг каждые 2 секунды, поэтому время рендеринга сокращается до 5 за 10 секунд.Вы можете понять, что производительность увеличилась вдвое.
блокировка списка
В соответствии с предыдущими требованиями, когда пользователь щелкает одно из данных в списке, список должен перестать прокручиваться. Поэтому я добавил блокировку прокруткиautoScrollLoack, функция этой блокировки заключается в том, чтобы выполняться, когда я нажимаю на элемент в спискеautoScrollLoack = trueСтраница не будет прокручиваться. Решение этого замка будет помещено вthis.scrollToBottom()В коде просто взгляните на код.
scrollToBottom(){
if(autoScrollLoack){
return;
}
...do something
},
этоautoScrollLoackНа странице он будет двусторонне связан с помощью переключателя, поэтому пользователь может контролировать состояние блокировки, изменяя выбранное состояние переключателя.На самом деле, после того, как замок установлен, если страница останавливается прокрутка из-за спроса, пользователь также это может воспринять, и он не прекратит прокрутку внезапно, что выглядит как ошибка.
перемещение фокуса
Ранее уже упоминалась функция перемещения фокуса, то есть при выделении фрагмента информации можно с помощью клавиш вверх и вниз наводить фокус на предыдущий или следующий, что на самом деле проще сделать.
<div
v-for="(item, idx) in list"
v-if="idx >= startIdx && idx <= endIdx"
:key="item.id"
:class="{'active':curIdx==idx}"
class="list-row"
@click="showDetail(item.id)">
Здесь вы можете видеть,activeЭто стиль списка, когда он сфокусирован. Логически поместите текущий выбранный элементindexназначатьcurIndex, интерфейсный шаблон управляет стилем посредством привязки vue к классу, а условием оценки являетсяcurIndex == index.
Функция фокусировки достигнута, следующим шагом является достижение эффекта перемещения фокуса с помощью клавиш вверх и вниз на клавиатуре. Эта функция очень проста. Мы можем полностью реализовать ее с помощью событий мониторинга, предоставляемых Vue. Конкретную реализацию вы можете найти на официальном сайте.keyup.
<div class="list-body-box" @scroll="listScroll" @keyup="moveFocus">
...
...
moveFocus(e){
let keyCode = Number(e.keyCode);
switch(keyCode){
case 38:
this.curIdx -= 1;
this.showDetail();
break;
case 40:
this.curIdx += 1;
this.showDetail();
break;
}
},
Этот код реализует перемещение фокуса вверх и вниз. В соответствии с требованиями нам нужно отображать детали журнала, соответствующие сфокусированному элементу, каждый раз, когда мы фокусируемся, и детали должны быть получены путем отправки запроса Ajax. Тут возникает проблема, есть сценарий: я хочу переместить текущий фокус вниз 10 раз через клавиатуру, и я буду вызывать 10 запросов в процессе непрерывного фокуса, это на самом деле ненужно.Во время быстрого движения этоcareподробный, мне просто нужно показать детали цели. Подводя итог, нам нужно добавить анти-шейк.
let detailTimer;
showDetail(id){
if(detailTimer){
clearTimeout(detailTimer);
}
detailTimer = setTimeOut(() => {
$.post('...',{
id:id
}).then((res) => {
do something...
});
},300);
}
Из вышеприведенной логики мы видим, что когда пользователь быстро перемещает фокус, запрос не будет запущен, На самом деле, это изменение значительно улучшает беглость пользователя.
Суммировать
На самом деле, разработка виртуальных списков относительно проста, но практическое значение относительно велико.В этом процессе будет задействовано много оптимизаций страниц.Заинтересованные детские туфли могут попробовать.