1. Общая идея
1. Источник идей
Я был занят работой в последнее время и давно не писал статьи, я не знаю, как это написать, поэтому давайте поговорим о том, почему я хочу развивать компоненты этой статьи. В компании есть система позиционирования. В принципе, блок позиционирования будет иметь одну точку позиционирования за одну минуту или меньше, поэтому будет много точек позиционирования в день. Если таблица хочет отображать данные за один день или даже за три дня сразу , то количество данных будет очень большим (может быть около 50 000 штук данных) Если мы отобразим больше столбцов, проблема зависаний таблицы будет очевидна. Платформа пользовательского интерфейса, выбранная веб-сайтом нашей компании,iview ,чтобы быть откровеннымiviewОстальные компонентыeasyuiПроизводительность таблиц и функции старых фреймворков, таких как эта, очень хороши. В конце концов, они претерпели множество оптимизаций. Табличный компонент очень расширяемый, и очень сложно добиться хорошей производительности и функциональности.
easyuiЭто фреймворк, идущий в ногу со временем.Однажды я зашел на его официальный сайт и обнаружил, что он выпущен на основе текущего популярногоvue, реагировать, угловойпользовательский интерфейс. Поэтому на этот раз я решил посмотреть на его форму на основе vue, поэтому я увидел этот компонент, прикрепленный к соединению.Woohoo. J easy UI.net/demo_v UE/68…. Я обнаружил, что он решает проблему блокировки больших данных за счет отложенной загрузки страниц. Это то, что я в принципе могу понять, но после прочтения у меня есть некоторые сомнения. Во-первых, если он отображает только часть данных, прокрутка полоса прокрутки.Когда данные загружаются снова, почему полоса прокрутки всегда такая длинная. Гениально, я включил режим разработчика и посмотрел html код табличной части
На первый взгляд я понимаю, что нижняя и верхняя части таблицы на рисунке являются причиной того, что высота полосы прокрутки остается неизменной, а средняя часть всегда загружает только 40 штук данных в соответствии с прокруткой полосы прокрутки. , чтобы таблица с большим объемом данных застряла.
2. Подтверждение идей
Тогда у нас может быть в основном идея, давайте посмотрим.
- Во-первых, мы можем думать, что эта таблица разделена на3 части[Верх таблицы :(top), область прокрутки таблицы :(screen), внизу таблицы :(bottom)].
- topа такжеbottomЧасть проблемы заключается в вычислении высоты.В принципе, можно определить, что при прокрутке полосы прокрутки получается положение прокрутки, а затем по общему объему данных иscreenЧасть объема данных рассчитываетсяtopа такжеbottomВысотавычисляемое свойство) здесь более уместно.
-
screenПервоначальная идея части реализации состоит в том, чтобы вычислить данные, которые должны быть загружены, исходя из высоты прокрутки. Однако, как сделать его более гладким, когда данных лишнее, у меня все еще есть некоторые сомнения в душе, поэтому я продолжил наблюдать за его html. Для лучшего выражения передний план Да Винчи открыл свою программу для рисования и начал рисовать (●'◡'●)
Если монстры Pipi переместят полосу прокрутки в положение, превышающее высоту загружаемых 20 фрагментов данных, мы удалим все 40 фрагментов данных с помощью нового метода обработки и вычислим 20 фрагментов данных выше и ниже текущее положение в соответствии с положением прокрутки. В этом процессе может быть процесс отбеливания стола, но я думаю, что с ним должен справиться слой-маска.
Основная идея есть, тогда приступим к ее реализации (●'◡'●).
Во-вторых, процесс реализации
1. Структура таблицы
Таблица состоит из 2 табличных тегов, первый — это заголовок, а второй — содержимое данных, что удобно для последующего расширения. Здесь лень не разделять шапку, внутренний контент и tr на компонент, чтобы сделать код более читабельным, а затем оптимизировать его.
2. Логическая реализация
Давайте поговорим непосредственно об основной логической части, сначала рассмотрим части props и data
props: {
loadNum: {
//默认加载行数
type: [Number, String],
default() {
return 20;
}
},
tdHeight: {
//表格行高
type: [Number, String],
default() {
return 40;
}
},
tableHeight: {
//表格高度
type: [Number, String],
default() {
return "200";
}
},
tableList: {
//所有表格数据
type: Array,
default() {
return [];
}
},
columns: {
//所有表格匹配规则
type: Array,
default() {
return [];
}
},
showHeader: {
type: Boolean,
default: true
}
},
data() {
return {
isScroll: 17,
showLoad: false,
columnsBottom: [], //实际渲染表格规则
showTableList: [], //实际显示的表格数据
loadedNum: 0, //实际渲染的数据数量
dataTotal: 0, //总数据条数
dataTop: 0, //渲染数据顶部的高度
scrollTop: 0, //滚动上下的距离
interval: null, //判断滚动是否停止的定时器
scrollHeight: 0, //数据滚动的高度
selectTr: -1 //选择的行
};
},
Тогда давайте посмотрим, что должно делать событие прокрутки.
//滚动条滚动 handleScroll(event) { let bottomScroll = document.getElementById("bottomDiv"); let topScroll = document.getElementById("topDiv"); if (bottomScroll.scrollTop > this.scrollTop) { //记录上一次向下滚动的位置 this.scrollTop = bottomScroll.scrollTop; //向下滚动 this.handleScrollBottom(); } else if (bottomScroll.scrollTop < this.scrollTop) { //记录上一次向上滚动的位置 this.scrollTop = bottomScroll.scrollTop; //向上滚动 this.handleScrollTop(); } else { //左右滚动 this.handleScrollLeft(topScroll, bottomScroll); } }
Сначала мы проходимscrollTopЭта переменная записывает положение вертикальной полосы прокрутки каждый раз при входе в событие прокрутки, если это значениепостоянныйТогда этот свитокпрокручивать влево и вправо,если это значениеувеличиватьсяпосмотри тогда этопрокрутить вниз, если это значениестать меньшетогда этопрокрутить вверх. Что нам нужно сделать при прокрутке влево и вправо, так это заставить заголовок перемещаться вместе с содержимым, чтобы можно было зафиксировать эффект перемещения заголовка влево и вправо и перемещения вверх и вниз.
//滚动条左右滚动
handleScrollLeft(topScroll, bottomScroll) {
//顶部表头跟随底部滚动
topScroll.scrollTo(bottomScroll.scrollLeft, topScroll.pageYOffset);
},
Если он движется вверх, мы должны сделать то, что улучшили в своем мышлении.Сначала посмотрите на код.
//滚动条向上滚动
handleScrollTop() {
//如果加载的数据小于默认加载的数据量
if (this.dataTotal > this.loadNum) {
let computeHeight = this.dataTop; //数据需要处理的时候的高度
if (
this.scrollTop < computeHeight &&
this.scrollTop >= computeHeight - this.loadNum * this.tdHeight
) {
this.showLoad = true;
//如果滚动高度到达数据显示顶部高度
if (this.dataTotal > this.loadedNum) {
//如果数据总数大于已经渲染的数据
if (this.dataTotal - this.loadedNum >= this.loadNum) {
//如果数据总数减去已经渲染的数据大于等于loadNum
this.dataProcessing(
this.loadNum,
this.loadedNum - this.loadNum,
"top"
);
} else {
this.dataProcessing(
this.dataTotal - this.loadedNum,
this.dataTotal - this.loadedNum,
"top"
);
}
}
} else if (
this.scrollTop <
computeHeight - this.loadNum * this.tdHeight
) {
this.showLoad = true;
let scrollNum = parseInt(this.scrollTop / this.tdHeight); //滚动的位置在第几条数据
if (scrollNum - this.loadNum >= 0) {
this.dataProcessing(this.loadNum * 2, scrollNum, "topAll");
} else {
this.dataProcessing(scrollNum + this.loadNum, scrollNum, "topAll");
}
}
}
},
- Во-первых, мы оцениваем, меньше ли загруженных данных, чем количество загруженных данных по умолчанию.Если это так, то логика не требуется, поскольку все данные были загружены.
- Определите, превышает ли высота прокрутки верхнюю позицию данных текущей части экрана и меньше ли она верхней позиции данных текущей части экрана за вычетом высоты загруженного объема данных по умолчанию, то есть мы упоминали первый случай ранее, затем это больше, чем данные текущей части экрана. Верхнее положение минус высота объема загружаемых данных по умолчанию - это второй случай.
- Если вы вводите два суждения, для this.showLoad устанавливается значение true, и слой маски включается, чтобы форма не становилась белой и не влияла на взаимодействие с пользователем, а подсказка загружалась.
- В первом случае, если верхняя часть данных меньше, чем данные загрузки по умолчанию, мы загружаем только данные оставшейся высоты.Если она больше, чем загрузка по умолчанию, загружаем данные загрузки по умолчанию this.loadNum
- Второй случай также является таким же суждением, но оценивается, больше ли this.loadNum*2, чем количество данных в верхней части данных, и загружаются только данные оставшейся высоты или данные this.loadNum *2 загружен.
//滚动条向下滚动
handleScrollBottom() {
let computeHeight =
this.dataTop +
this.loadedNum * this.tdHeight -
(this.tableHeight - this.tdHeight - 3); //数据需要处理的时候的高度
if (
this.scrollTop > computeHeight &&
this.scrollTop <= computeHeight + this.tdHeight * this.loadNum
) {
this.showLoad = true;
//如果滚动高度到达数据显示底部高度
if (this.dataTotal > this.loadedNum) {
//如果数据总数大于已经渲染的数据
if (this.dataTotal - this.loadedNum >= this.loadNum) {
//如果数据总数减去已经渲染的数据大于等于20
this.dataProcessing(
this.loadedNum - this.loadNum,
this.loadNum,
"bottom"
);
} else {
this.dataProcessing(
this.dataTotal - this.loadedNum,
this.dataTotal - this.loadedNum,
"bottom"
);
}
}
} else if (
this.scrollTop >
computeHeight + this.tdHeight * this.loadNum
) {
this.showLoad = true;
let scrollNum = parseInt(this.scrollTop / this.tdHeight); //滚动的位置在第几条数据
if (scrollNum + this.loadNum <= this.dataTotal) {
this.dataProcessing(scrollNum, this.loadNum * 2, "bottomAll");
} else {
this.dataProcessing(
scrollNum,
this.dataTotal - scrollNum + this.loadNum,
"bottomAll"
);
}
}
},
После расчета есть 4 случая, и вычисляется соответствующий объем данных, которые необходимо удалить и добавить. Давайте посмотрим, что делает функция dataProcessing.
//上下滚动时数据处理
dataProcessing(topNum, bottomNum, type) {
let topPosition = parseInt(this.dataTop / this.tdHeight);
if (type === "top") {
this.showTableList.splice(this.loadedNum - bottomNum, bottomNum); //减去底部数据
for (var i = 1; i <= topNum; i++) {
//加上顶部数据
let indexNum = topPosition - i;
this.tableList[indexNum].index = indexNum + 1;
this.showTableList.unshift(this.tableList[indexNum]);
}
this.loadedNum = this.loadedNum + topNum - bottomNum; //重新计算实际渲染数据条数
this.dataTop = this.dataTop - topNum * this.tdHeight; //重新计算渲染数据的高度
document.getElementById("bottomDiv").scrollTop =
document.getElementById("bottomDiv").scrollTop +
bottomNum * this.tdHeight;
this.scrollTop = document.getElementById("bottomDiv").scrollTop;
} else if (type == "bottom") {
this.showTableList.splice(0, topNum); //减去顶部数据
for (var i = 0; i < bottomNum; i++) {
//加上底部数据
let indexNum = topPosition + this.loadedNum + i;
this.tableList[indexNum].index = indexNum + 1;
this.showTableList.push(this.tableList[indexNum]);
}
this.loadedNum = this.loadedNum - topNum + bottomNum; //重新计算实际渲染数据条数
this.dataTop = this.dataTop + topNum * this.tdHeight; //重新计算渲染数据的高度
document.getElementById("bottomDiv").scrollTop =
document.getElementById("bottomDiv").scrollTop -
topNum * this.tdHeight;
this.scrollTop = document.getElementById("bottomDiv").scrollTop;
} else if (type == "bottomAll") {
this.showTableList = []; //减去顶部数据
let scrollNum = topNum;
for (var i = 0; i < bottomNum; i++) {
//加上底部数据
let indexNum = scrollNum - this.loadNum + i;
this.tableList[indexNum].index = indexNum + 1;
this.showTableList.push(this.tableList[indexNum]);
}
this.loadedNum = bottomNum; //重新计算实际渲染数据条数
this.dataTop = (scrollNum - this.loadNum) * this.tdHeight; //重新计算渲染数据的高度
this.scrollTop = document.getElementById("bottomDiv").scrollTop;
} else if (type == "topAll") {
this.showTableList = []; //减去顶部数据
let scrollNum = bottomNum;
for (var i = 0; i < topNum; i++) {
//加上底部数据
let indexNum = scrollNum - topNum + this.loadNum + i;
this.tableList[indexNum].index = indexNum + 1;
this.showTableList.push(this.tableList[indexNum]);
}
this.loadedNum = topNum; //重新计算实际渲染数据条数
this.dataTop = (scrollNum - topNum + this.loadNum) * this.tdHeight; //重新计算渲染数据的高度
this.scrollTop = document.getElementById("bottomDiv").scrollTop;
}
this.showLoad = false;
},
- Сначала удаляем данные, которые мы рассчитали ранее и должны быть удалены.spliceМетод удаляет соответствующие данные, а затем проходит через простой цикл for, если он прокручивается вверх, то данные должны быть добавлены в начало.unshiftметод, если он прокручивается вниз, мы должны добавить его внизу, который мы используемpushметод.
- После обработки данных нам также необходимо пересчитать фактическое количество отрендеренных данных и изменить значение loadNum на количество данных, отображаемых сейчас.
- Пересчитайте высоту визуализированных данных, чтобы вычислить высоту верхней части данных, отображаемых теперь dataTop.
- потому чтоtopа такжеbottomЭто изменение приведет к изменению значения таблицы scrollTop. В настоящее время нам нужно динамически перемещать полосу прокрутки в правильное положение.
Наконец, давайте поговорим о том, что мы рассмотрели ранееtopа такжеbottom, в начале мы думали, что для этого нужно использовать вычисляемые свойства, факт также показывает, что это так, давайте посмотрим на код
computed: {
tableOtherTop() {
//表格剩余数据顶部高度
return this.dataTop;
},
tableOtherBottom() {
//表格剩余数据底部高度
return (
this.dataTotal * this.tdHeight -
this.dataTop -
this.loadedNum * this.tdHeight
);
}
},
Это гарантирует, чтоtopа такжеbottomИзменение высоты может вызвать изменение таблицы.
topдолжна быть высотой верхней части отображаемых данных (dataTop).
bottomВысота должна быть общей высотой данных - высотой отображаемых данных (this.loadedNum * this.tdHeight)-topвысота.
Наконец, давайте взглянем на рендеры
обновление 2020-3-26
- Используйте функцию анти-дрожания, чтобы решить ситуацию с мерцанием и прыганием полосы прокрутки во время процесса перетаскивания. В то же время каждый раз, когда запускается прокрутка, выносится дружественное суждение, чтобы определить, превышает ли текущая прокрутка диапазон данных и анимацию загрузки.
/** * @typedef {Object} Options -配置项 * @property {Boolean} leading -开始是否需要额外触发一次 * @property {this} context -上下文 **/ //使用Proxy实现函数防抖 proxy( func, time, options = { leading: true, context: null } ) { let timer; let _this = this; let handler = { apply(target, _, args) { //代理函数调用 let bottomScroll = document.getElementById("bottomDiv"); let topScroll = document.getElementById("topDiv"); if (bottomScroll.scrollTop == _this.scrollTop) { //左右滚动 _this.handleScrollLeft(topScroll, bottomScroll); return; } // 和闭包实现核心逻辑相同 if (!options.leading) { if (timer) return; timer = setTimeout(() => { timer = null; Reflect.apply(func, options.context, args); }, time); } else { if (timer) { _this.needLoad(bottomScroll); clearTimeout(timer); } timer = setTimeout(() => { Reflect.apply(func, options.context, args); }, time); } } }; return new Proxy(func, handler); }, //是否显示加载中 needLoad(bottomScroll) { if ( Math.abs(bottomScroll.scrollTop - this.scrollTop) > this.tdHeight * this.loadNum ) { this.showLoad = true; //显示加载中 this.scrollTop = bottomScroll.scrollTop; } } - Отслеживайте изменения высоты таблицы, чтобы устранить ошибку, из-за которой некоторый контент не отображается из-за изменения высоты.
- Этот компонент формы был опубликован дляnpmДобро пожаловать в использование, задавайте вопросы
Суммировать
Самая хлопотная часть разработки этого компонента — разобраться в различных ситуациях, а затем написать различные расчеты, чтобы гарантировать отсутствие ошибок. В процессе разработки у меня также возникла идея, что я хочу сам разработать простой компонент таблицы, но я чувствовал, что мой личный уровень ограничен, но я также во многих местах предвидел, и я буду расширять этот компонент когда у меня будет время в будущем. Давай~~~// /(^v^)\\\~~~.
Вот мойgithubадресGitHub.com/GitHub30789…, я загрузил проект.Если вам он нравится, вы можете дать мне старт, спасибо (●'◡'●), может быть еще много проблем, и я надеюсь получить ваш совет.