предисловие
За ним код и демо-адрес
В повседневной разработке, помимо использования готовых плагинов, есть еще много задач, которые можно решить только самостоятельно. Сначала бросьте проблему.Когда данные выпадающего списка достигают тысяч или даже десятков тысяч, браузер серьезно застрянет. Взгляните на пример ниже
Как показано на рисунке, объем данных достигает2Wполоскапростые тестовые данные(на странице больше ничего нет), щелчок по раскрывающемуся списку загрузки занял около5sвремя. Когда такое происходит, у меня на душе очень тяжело, разве это не играет со мной?
Решения
Эта проблема на самом деле является той же проблемой производительности, что и табличные данные.Решение таблицы заключается в уменьшении объема данных, передаваемых страницей через пейджер. Итак, как решить выпадающий список? Обычно мы загружаем все данные, вытащенные за один раз.Для текущей проблемы идея та же, используя пейджинг для решения проблемы с производительностью страницы. Проблема возникает снова: пейджер можно щелкнуть, но нельзя щелкнуть раскрывающийся список, поэтому это важное событие может быть реализовано только путем прослушивания события прокрутки.
Сначала набросок:
- прокрутка монитора
- Загружать данные назад при прокрутке вниз
- Загружать данные вперед при прокрутке вверх
- данные в и из
начинается хорошая игра
1. Следите за прокруткой
<el-select class="remoteSelect" v-scroll v-model="value">
<el-option :value="item.id" v-for="item in list" :key="item.id">{{item.name}}</el-option>
</el-select>
Здесь основано на vue и element-uiel-selectРеализована прокрутка монитора. Вот способ контролировать прокрутку с помощью пользовательских инструкций
// directives目录下index.js文件
import Vue from 'vue'
export default () => {
Vue.directive('scroll', {
bind (el, binding) {
// 获取滚动页面DOM
let SCROLL_DOM = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap')
SCROLL_DOM.addEventListener('scroll', function () {
console.log('scrll')
})
}
})
}
Зарегистрируйтесь и используйте глобальный метод Vue.use() в main.js
import Directives from './directives'
Vue.use(Directives)
В это время вы можете пролистать страницу, чтобы увидеть журнал контрольной печати, а это значит, что мониторинг вступил в силу.Тогда засучите рукава и приступайте к сушке.
2. Загружать данные назад при прокрутке вниз
Сначала определите, следует ли прокручивать вверх или прокручивать вниз
- Запишите последнюю позицию прокрутки
- Сравните текущую позицию с последней позицией прокрутки
Глобальное местоположение записывается через общедоступную переменную через
scrollTop
Метод получает текущую позицию прокрутки и записывает ее в общедоступную переменную.scrollPosition
внутри
bind (el, binding) {
// 获取滚动页面DOM
let SCROLL_DOM = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap')
let scrollPosition = 0
SCROLL_DOM.addEventListener('scroll', function () {
// 当前的滚动位置 减去 上一次的滚动位置
// 如果为true则代表向上滚动,false代表向下滚动
let flagToDirection = this.scrollTop - scrollPosition > 0
// 记录当前的滚动位置
scrollPosition = this.scrollTop
console.log(flagToDirection ? '滚动方向:下' : '滚动方向:上')
})
}
В настоящее время направление прокрутки известно, и затем выполняется соответствующая обработка в соответствии с направлением прокрутки. Расскажите компоненту о поведении прокрутки
...省略
// 记录当前的滚动位置
scrollPosition = this.scrollTop
// 将滚动行为告诉组件
binding.value(flagToDirection)
принятие событиясуществует
v-scroll
получить событие в командеv-scroll="handleScroll"
, в методеhandleScroll
Обработка поведения прокрутки. Далее вам нужно только запросить данные для прокрутки вниз в этом событии.
/*********************************
** Fn: handleScroll
** Intro: 处理滚动行为
** @params: param 为true代表向下滚动
** @params: param 为false代表向上滚动
*********************************/
handleScroll (param) {
if (param) {
// 请求下一页的数据
this.list.push(...this.ajaxData(++this.pageIndex))
}
},
Здесь реализована загрузка прокрутки. Просто загрузка слишком частая.Если скроллить быстро, будет выдаваться несколько запросов фоновых данных одновременно.В некоторых интенсивных браузерах ajax будет разрабатываться и ставиться в очередь одновременно, что не идеально. Как это контролировать? Запускается другим способом
handleScroll
Событие срабатывает, когда положение прокрутки находится на определенной высоте от низа прокручиваемой страницы, например, только от низа страницы100px
курокhandleScroll
событие
-
scrollHeight
получить высоту прокрутки - в 100px снизу
// 记录当前的滚动位置
scrollPosition = this.scrollTop
const LIMIT_BOTTOM = 100
// 记录滚动位置距离底部的位置
let scrollBottom = this.scrollHeight - (this.scrollTop + this.clientHeight) < LIMIT_BOTTOM
// 如果已达到指定位置则触发
if (scrollBottom) {
// 将滚动行为告诉组件
binding.value(flagToDirection)
}
Благодаря изменению длины данных мы можем узнать, что триггерное событие было явно гармоничным.Этот эффект аналогичен способу ленивой загрузки мобильных телефонов, и данные будут непрерывно накладываться.
намекать:Будет ошибка, что ajax является асинхронным, если этот запрос ajax занимает1sТолько когда данные будут возвращены, а прокрутка в это время продолжится, будут инициированы несколько событий запроса. Как избежать этой ситуации? Ответ заключается в том, чтобы добавить флаг, установить для него значение false перед запросом и установить его в значение true после запроса. Этот флаг проверяется первым для каждого запроса. Если false предотвращает событие.
полузащита
Взгляните на нашу схему
- прокрутка монитора
- Загружать данные назад при прокрутке вниз
- Загружать данные вперед при прокрутке вверх
- данные в и из
Пока мы выполнили только шаги ① и ②. Если ваши потребности удовлетворены, вы можете закончить чтение. Если это вам немного поможет, ставьте лайк и уходите.
То, что я сказал ранее, является лишь базовой операцией, и я еще не начал на ней сосредотачиваться. Как насчет отсутствия давления производительности?
Пойдем домой после работы на ужин. Продолжайте писать в выходные --2018-11-09 18:15
великолепная сегментация
как выходные,все придет, как ожидалось. --2018-11-10 08:30
3. Загружать данные вперед при прокрутке вверх
существуетhandleScroll
средний параметр оценкиparam
Мы изучили поведение прокрутки, но раньше мы ограничивали только время срабатывания прокрутки вниз, а теперь мы усовершенствовали время срабатывания прокрутки вверх. Точно так же сначала используйте расстояние от вершины100px
при срабатывании.
пока текущая позиция прокрутки
scrollTop
меньше, чем100px
просто вызватьhandleScroll
событие
// 如果向下滚动 并且距离底部只有100px
if (flagToDirection && scrollBottom) {
// 将滚动行为告诉组件
binding.value(flagToDirection)
}
// 如果是向上滚动 并且距离顶部只有100px
if (!flagToDirection && this.scrollTop < LIMIT_BOTTOM) {
binding.value(flagToDirection)
}
существуетhandleScroll
В случае, если мы уже можем обнаружить поведение прокрутки вверх, и время запуска соответствует ожидаемому.
4. Данные приходят и уходят
Как насчет отсутствия давления производительности? Прямо в этот момент. Взгляните на картинку одним взглядом (эту визуализацию найти непросто):
- Прокрутите вниз (каждый щелчок на рисунке представляет собой триггер для прокрутки для загрузки данных)
list
Всегда есть только определенный объем данных. Независимо от того, насколько велик объем данных, что я могу сделать?
Давайте посмотрим, как это сделать
Как сохранить длину этого массива? Легко сказать, что есть входы и выходы, но реализация непростая.
Предположим, что наш текущий контейнер массива может содержать до 4 страниц данных, по 100 фрагментов данных на страницу. пройти черезpageLimit
Параметр для ограничения длины массива, который нам нужно поддерживать, здесь установлен равным 4.
Как мы узнаем, какая страница загружена в данный момент при прокрутке вниз или прокрутке вверх?
Итак, нам нужна таблица записей
pageMap
Для записи номера страницы таблица номеров страниц связана с текущим объектом данных.list
соответствовать. Корреспонденция следующим образом.
pageLimit: 4
pageMap: [1, 2, 3, 4]
list:['第一页的数据', '第二页的数据', '第三页的数据', '第四页的数据']
Рендеринг (в настоящее время прокрутка ненаучна, шаги правильные, за ними следует оптимизированная прокрутка):
/*********************************
** Fn: handleScroll
** Intro: 处理滚动行为
** @params: param 为true代表向下滚动
** @params: param 为false代表向上滚动
*********************************/
handleScroll (param) {
if (param) {
if (this.pageMap.length >= this.pageLimit) {
// 当长度相等的时候, 绝对不能超出长度 则有进必有出
// 删除 pageMap 列表的第一个元素
this.pageMap.shift()
// 对应删除list中一页的数据量
this.list.splice(0, this.pageSize)
}
++this.pageIndex
this.pageMap.push(this.pageIndex)
// 请求下一页的数据
this.list.push(...this.ajaxData(this.pageIndex))
// 同步记录页码
} else {
// 如果在向上滚动时,如果还没有到达第一页则继续加载。 如果已到达则停止加载
if (this.pageMap[0] > 1) {
// 向上滚动,取出pageMap中第一个元素值减1
this.pageIndex = this.pageMap[0] - 1
// 同步设置分页
// ①先删除最后一个元素
this.pageMap.pop()
// ②将新元素添加在头部
this.pageMap = [this.pageIndex, ...this.pageMap]
// ①删除list中最后一页的数据
this.list.splice(-this.pageSize, this.pageSize)
// ②将新数据添加在头部位置
this.list = [...this.ajaxData(this.pageIndex), ...this.list]
} else return false
}
}
Давайте сначала сюда напишем, опять пора обедать 2018-11.10 12:01
Добрый день, зимнее солнце пригревает~ 2018-11-10 13:04Оптимизировать прокрутку
Далее заполним яму, оставленную выше.Когда данные достигнут заданной длины, общий объем данных не изменится, тогда общая высота прокруткиscrollHeight
Это также исправлено Это то, что, хотя данные входят и выходят, это не влияет на положение прокрутки.scrollTop
Это больше не будет иметь никакого влияния.Например, эффект на динамической картинке выше будет прокручиваться до конца, но в это время это не конец страницы, но это заставляет пользователей ошибочно думать, что это в конце~ ~ Эта проблема немного серьезна, немного серьезна~
Оптимизация:
- Возвращайте текущую позицию прокрутки в середину общей высоты прокрутки после каждой загрузки данных.
В этот момент нам нужно прокрутить
dom
и высота среднего положения через пользовательскую директивуv-scroll
Брошенный, при добавлении данных в голову или добавлении данных в хвост положение прокрутки позиционируется в среднее положение.
Выбрасывает DOM и прокручивает среднюю позицию
// directives目录下index.js文件
// 如果向下滚动 并且距离底部只有100px
if (flagToDirection && scrollBottom) {
// 将滚动行为告诉组件
binding.value(flagToDirection, SCROLL_DOM, this.scrollHeight / 2)
}
// 如果是向上滚动 并且距离顶部只有100px
if (!flagToDirection && this.scrollTop < LIMIT_BOTTOM) {
binding.value(flagToDirection, SCROLL_DOM, this.scrollHeight / 2)
}
когда
pageMap
(соответствоватьlist
длина) доpageLimit
Длина, сброс положения прокрутки DOM при добавлении или удалении данных.
/*********************************
** Fn: handleScroll
** Intro: 处理滚动行为
** @params: param 为true代表向下滚动 为false代表向上滚动
** @params: el 滚动DOM
** @params: middlePosition 滚动列表的中间位置
*********************************/
handleScroll (param, el, middlePosition) {
if (param) {
if (this.pageMap.length >= this.pageLimit) {
....省略代码
this.list.splice(0, this.pageSize)
// 回滚到中间位置
el.scrollTop = middlePosition
}
....省略代码
} else {
// 如果在向上滚动时,如果还没有到达第一页则继续加载。 如果已到达则停止加载
if (this.pageMap[0] > 1) {
....省略代码
this.list = [...this.ajaxData(this.pageIndex), ...this.list]
// 回滚到中间位置
el.scrollTop = middlePosition
} else return false
}
},
Эффект как показано выше, должен ли он закончиться? Студенты, которые внимательно наблюдали, снова нашли пасхальные яйца. В момент прокрутки и перехода данные, которые видит первоначальный пользователь, уже не являются данными, которые первоначальный пользователь видит после перехода.да я..... Эта проблема немного серьезная,паника
2. Оптимизация критической точки качения
критическая точка - дальность каченияОбщая высотаКогда верх или низ находится на определенном расстоянии, срабатывает критическая точка handleScroll, то есть константаLIMIT_BOTTOM
. ранее определенныйconst LIMIT_BOTTOM = 100
Для 100 в этом нет смысла, так что подойдите к серьезной критической точке.
Состояние расчесывания
- каждый раз возвращаться к
1/2 scrollHeight
позиция - Позиция каждого изменения данных
(1 / pageLimit) * scrollHeight
, что показано здесь1/4 * scrollHeight
- установить неизвестноеXкритическая точка для прыжка
- Переломный момент — это то, что видит пользователь, прежде чем прыгнуть.
-
1/2 scrollHeight
это позиция после прыжка пользователя
Выражение: x + (1/4 * scrollHeight) = (1/2 scrollHeight)
Эффект почти тот же.Если вы хотите, чтобы пользователь видел позицию ниже в конце, вы можете установитьx = (1/4 * scrollHeight), т.е.
const LIMIT_BOTTOM = this.scrollHeight / 4
Затем снова откроем его и посмотрим на ситуацию с прокруткой:
const LIMIT_BOTTOM = this.scrollHeight / 4.2
Эпилог
История наконец заканчивается здесь.поставить лайкПодбодри меня~
Создайте новый репозиторий на github для загрузки кода:
- демонстрационный вид:demo
- просмотр кода github:портал
- Это очень полезно, но никто этого не ценитПользовательская директива Vue реализует ограничение ввода положительное целое число
- Зачем переупаковывать AJAX?
- Уведомление об авторских правах: Эта статья была впервые опубликована в Nuggets, если вам нужно перепечатать, пожалуйста, укажите источник.
Лучшее время посадить дерево было десять лет назад, затем сейчас. -- Неважно, кто это сказал.