前端虛擬滾動列表實現(xiàn)代碼(vue虛擬列表)
前言
方法一利用瀏覽器原生api去實現(xiàn),可以實現(xiàn)不等高的列表虛擬滾動,intersectionObserver 多用于圖片懶加載,虛擬滾動列表
方法二通過監(jiān)聽滾動條的位置,去計算顯示的內(nèi)容,這里需要列表等高,當然不等高也可以計算,稍微改改
前端虛擬滾動列表(方法一:利用IntersectionObserver api 簡單)
- IntersectionObserver可以用來自動監(jiān)聽元素是否進入了設(shè)備的可視區(qū)域之內(nèi),而不需要頻繁的計算來做這個判斷。由于可見(visible)的本質(zhì)是,目標元素與視口產(chǎn)生一個交叉區(qū),所以這個 API 叫做"交叉觀察器"IntersectionObserver 方案多用于圖片懶加載或者列表虛擬滾動
IntersectionObserver 是瀏覽器原生提供的構(gòu)造函數(shù),接受兩個參數(shù): callback:可見性發(fā)現(xiàn)變化時的回調(diào)函數(shù) option:配置對象(可選)。構(gòu)造函數(shù)的返回值是一個觀察器實例。實例一共有4個方法:
observe:開始監(jiān)聽特定元素
unobserve:停止監(jiān)聽特定元素
disconnect:關(guān)閉監(jiān)聽工作
takeRecords:返回所有觀察目標的對象數(shù)組
callback 參數(shù)
目標元素的可見性變化時,就會調(diào)用觀察器的回調(diào)函數(shù)callback。
callback一般會觸發(fā)兩次。一次是目標元素剛剛進入視口,另一次是完全離開視口。
const io = new IntersectionObserver((changes, observer) => {
console.log(changes);
console.log(observer);
});- options
- threshold: 決定了什么時候觸發(fā)回調(diào)函數(shù)。它是一個數(shù)組,每個成員都是一個門檻值,默認為[0],即交叉比例(intersectionRatio)達到0時觸發(fā)回調(diào)函數(shù)。用戶可以自定義這個數(shù)組。比如,[0, 0.25, 0.5, 0.75, 1]就表示當目標元素 0%、25%、50%、75%、100% 可見時,會觸發(fā)回調(diào)函數(shù)。
- root: 用于觀察的根元素,默認是瀏覽器的視口,也可以指定具體元素,指定元素的時候用于觀察的元素必須是指定元素的子元素
- rootMargin: 用來擴大或者縮小視窗的的大小,使用css的定義方法,10px 10px 30px 20px表示top、right、bottom 和 left的值
————————————————
這里是后面補充的簡單還原了下面方法二的例子,重點在60行,從哪兒看就可以
<template>
<div class="big-box">
<div class="download-box txt" id="scrollable-div">
<div v-for="(item, index) in props.seqText" :key="index" class="line-box">
<template v-if="index === 0 && start === 0">
<div :class="{ 'text-title': props.collapsed, 'text-title-samll': !props.collapsed }">
{{ item }}
</div>
</template>
<template v-else>
<div :class="{ 'text-number': props.collapsed, 'text-number-samll': !props.collapsed }">
{{ calLine(item, index + start) }}
</div>
<div
:class="{ 'text-box': props.collapsed, 'text-box-samll': !props.collapsed }"
:data="item"
>
''
</div>
<div :class="{ 'text-number2': props.collapsed, 'text-number2-samll': !props.collapsed }">
{{ endRow(item, index + start) }}
</div>
</template>
</div>
</div>
</div>
<SearchBox :againFind="againFind" />
</template>
<script lang="ts" setup>
import { watch, onMounted, PropType, reactive, ref } from 'vue';
import SearchBox from '/@/components/SearchBox/index.vue';
import { message } from 'ant-design-vue';
const props = defineProps({
collapsed: {
type: Boolean,
default: true,
},
seqText: {
type: Array as PropType<string[]>,
default: [''],
},
});
let width = 100;
const geneTexts: Array<string> = [];
const data = reactive({
geneTexts,
});
const calLine = (item: any, index: number) => {
return width * (index - 1) + 1;
};
const endRow = (item: any, index: number) => {
return width * index;
};
// 這里是核心要點
const io = new IntersectionObserver(
(entries) => {
console.log(entries);
for (const entry of entries) {
if (entry.isIntersecting) {
const elTxt = entry.target;
// console.log(elTxt.getAttribute('data'));
elTxt.innerHTML = elTxt.getAttribute('data');
io.unobserve(elTxt);
}
}
},
{
root: document.getElementById('scrollable-div'),
// rootMargin: 0,
threshold: 0.5,
},
);
setTimeout(() => {
const elList = document.querySelectorAll('.text-box');
console.log(elList);
elList.forEach((element) => {
io.observe(element);
});
}, 1000);
const againFind = ref(1);
let start = ref(0);
</script>
<style lang="less" scoped>
// @import '/@/assets/styles/views/medaka.less';
.big-box {
background: #282c34;
padding: 30px 20px;
height: 870px;
}
.download-box {
width: 100%;
// padding: 0px 20px;
// outline: 1px solid rgb(17, 0, 255);
overflow: hidden;
.line-box {
.flex-type(flex-start);
height: 30px;
}
&.txt {
background: #282c34;
color: #fff;
height: 810px;
overflow: auto;
.el-row {
display: flex;
align-items: center;
margin-bottom: 10px;
margin: auto;
font-size: 22px;
}
}
}
@media screen and (min-width: 1842px) {
.text-box-samll {
letter-spacing: 1.5px;
font-size: 15px;
}
.text-number-samll {
min-width: 60px;
font-size: 15px;
}
.text-number2-samll {
margin-left: 20px;
min-width: 60px;
font-size: 15px;
}
.text-title-samll {
font-size: 15px;
}
.text-box {
font-size: 22px;
// letter-spacing: 3px;
}
.text-number {
min-width: 100px;
font-size: 22px;
}
.text-number2 {
margin-left: 20px;
min-width: 100px;
font-size: 22px;
}
.text-title {
font-size: 22px;
}
}
@media screen and (min-width: 1600px) and (max-width: 1841px) {
.text-box-samll {
font-size: 15px;
}
.text-number-samll {
min-width: 40px;
font-size: 15px;
}
.text-number2-samll {
margin-left: 20px;
min-width: 40px;
font-size: 15px;
}
.text-title-samll {
font-size: 15px;
}
.text-box {
font-size: 20px;
// letter-spacing: 1.2px;
}
.text-number {
min-width: 60px;
font-size: 20px;
}
.text-number2 {
margin-left: 20px;
min-width: 60px;
font-size: 20px;
}
.text-title {
font-size: 20px;
}
}
@media screen and (min-width: 1443px) and (max-width: 1599px) {
.text-box-samll {
font-size: 13px;
}
.text-number-samll {
min-width: 40px;
font-size: 13px;
}
.text-number2-samll {
margin-left: 20px;
min-width: 40px;
font-size: 13px;
}
.text-title-samll {
font-size: 13px;
}
.text-box {
font-size: 18px;
// letter-spacing: 1.2px;
}
.text-number {
min-width: 60px;
font-size: 15px;
}
.text-number2 {
margin-left: 20px;
min-width: 60px;
font-size: 18px;
}
.text-title {
font-size: 18px;
}
}
@media screen and (max-width: 1442px) {
.text-box-samll {
font-size: 11px;
}
.text-number-samll {
min-width: 40px;
font-size: 11px;
}
.text-number2-samll {
margin-left: 20px;
min-width: 40px;
font-size: 11px;
}
.text-title-samll {
font-size: 11px;
}
.text-box {
font-size: 16px;
// letter-spacing: 1.2px;
}
.text-number {
min-width: 60px;
font-size: 15px;
}
.text-number2 {
margin-left: 20px;
min-width: 60px;
font-size: 16px;
}
.text-title {
font-size: 16px;
}
}
</style>
前端虛擬滾動列表(方法二:監(jiān)聽滾動計算 麻煩)
在大型的企業(yè)級項目中經(jīng)常要渲染大量的數(shù)據(jù),這種長列表是一個很普遍的場景,當列表內(nèi)容越來越多就會導(dǎo)致頁面滑動卡頓、白屏、數(shù)據(jù)渲染較慢的問題;大數(shù)據(jù)量列表性能優(yōu)化,減少真實dom的渲染
看圖:綠色是顯示區(qū)域,綠色和藍色中間屬于預(yù)加載:解決滾動閃屏問題;大致了解了流程在往下看;

實現(xiàn)效果:
先說一下你看到這么多真實dom節(jié)點是因為做了預(yù)加載,減少滾動閃屏現(xiàn)象,這里寫了300行,可以根據(jù)實際情況進行截取

實現(xiàn)思路:
虛擬列表滾動大致思路:兩個div容器
外層:外部容器用來固定列表容器的高度,同時生成滾動條
內(nèi)層:內(nèi)部容器用來裝元素,高度是所有元素高度的和
外層容器鼠標滾動事件 dom.scrollTop 獲取滾動條的位置
根據(jù)每行列表的高以及當前滾動條的位置,利用slice() 去截取當前需要顯示的內(nèi)容
重點:滾動條的高度是有內(nèi)層容器的paddingBottom 和 paddingTop 屬性頂起來了,確保滾動條位置的準確性
這里鼠標上下滾動會出現(xiàn)閃屏問題:解決方案如下:
方案一: 預(yù)加載:
向下預(yù)加載:
比如div滾動區(qū)域顯示30行,就預(yù)加載 300行( 即這里 slice(startIndex,startIndex + 300) ),向上預(yù)加載:
在滾動監(jiān)聽事件函數(shù)中(computeRow)判斷inner的paddingTop和paddingBottom即可當然這里的download-box的padding有30px像素,在加一個div,overflow:hidded就解決了
方案二:縮小滾動范圍或者節(jié)流時間縮短,這里寫的500ms
具體代碼
<template>
<div class="enn">
<div class="download-box txt" id="scrollable-div" @scroll="handleScroll">
<div id="inner">
<div v-for="(item, index) in data2" :key="index" class="line-box">
<div :class="{ 'text-box': props.collapsed, 'text-box-samll': !props.collapsed }">
{{ item }}
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, PropType, ref } from 'vue';
import { useText } from './hooks/useText';
const props = defineProps({
baseData: {
type: Object as PropType<{
taskId: string;
barcodeName: string;
}>,
default: {},
},
collapsed: {
type: Boolean,
default: true,
},
type: {
type: Boolean,
default: false,
},
});
const { data } = useText(props.type);
// 這里大數(shù)據(jù)量數(shù)組是 data.geneTexts
/**
* 虛擬列表滾動大致思路:兩個div容器
*
* 外層:外部容器用來固定列表容器的高度,同時生成滾動條
*
* 內(nèi)層:內(nèi)部容器用來裝元素,高度是所有元素高度的和
*
* 外層容器鼠標滾動事件 dom.scrollTop 獲取滾動條的位置
*
* 根據(jù)每行列表的高以及當前滾動條的位置,利用slice() 去截取當前需要顯示的內(nèi)容
*
* 重點:滾動條的高度是有內(nèi)層容器的paddingBottom 和 paddingTop 屬性頂起來了,確保滾動條位置的準確性
*
* 這里鼠標上下滾動會出現(xiàn)閃屏問題:解決方案如下:
*
* 方案一: 預(yù)加載:
*
* 向下預(yù)加載:
* 比如div滾動區(qū)域顯示30行,就預(yù)加載 300行( 即這里 slice(startIndex,startIndex + 300) ),
*
* 向上預(yù)加載:
* 在滾動監(jiān)聽事件函數(shù)中(computeRow)判斷inner的paddingTop和paddingBottom即可
*
* 當然這里的download-box的padding有30px像素,在加一個div,overflow:hidded就解決了
*
* 方案二:縮小滾動范圍或者節(jié)流時間縮短,這里寫的500ms
*
*
*/
let timer_throttle: any;
const throttle = (func: Function, wait?: number) => {
wait = wait || 500;
if (!timer_throttle) {
timer_throttle = setTimeout(() => {
func.apply(this);
timer_throttle = null;
}, wait);
}
};
// 鼠標滾動事件
const handleScroll = (event: any) => throttle(computeRow, 100);
// 計算當前顯示tab
const computeRow = () => {
// console.log('距離頂部距離', window.scrollY, geneTexts);
let scrollableDiv = document.getElementById('scrollable-div');
let topPosition = scrollableDiv.scrollTop;
let leftPosition = scrollableDiv.scrollLeft;
console.log('垂直滾動位置:', topPosition, '水平滾動位置:', leftPosition);
const startIndex = Math.max(0, Math.floor(topPosition / 30));
const endIndex = startIndex + 300;
data2.value = data.geneTexts.slice(startIndex, endIndex);
let inner = document.getElementById('inner');
if (topPosition < 2700) {
// 向上預(yù)計加載,這里判斷了三個高度,可以多判斷幾個,增加流暢度
inner.style.paddingTop = topPosition + 'px';
inner.style.paddingBottom = (data.geneTexts.length + 2) * 30 - topPosition + 'px';
} else if (topPosition + data2.value.length * 30 >= data.geneTexts.length * 30) {
// 這里 9000 是 內(nèi)層div的高度 30 * 300 理解div高度是 padding+div內(nèi)容高度
inner.style.paddingTop = topPosition - 900 + 'px'; //900 是div的高度
inner.style.paddingBottom = 0 + 'px';
} else {
inner.style.paddingTop = topPosition - 2700 + 'px';
inner.style.paddingBottom = (data.geneTexts.length + 2) * 30 + 2700 - topPosition + 'px';
}
};
const data2 = ref([]);
const init = () => {
data2.value = data.geneTexts.slice(0, 300);
let inner = document.getElementById('inner');
inner.style.paddingTop = 0 + 'px';
inner.style.paddingBottom = (data.geneTexts.length + 2) * 30 - 900 + 'px';
};
</script>
<style lang="less" scoped>
.button-box {
margin-bottom: 25px;
.flex-type(flex-end);
:deep(.ant-btn) {
margin-left: 10px;
}
}
.enn {
background: #282c34;
outline: 1px solid red;
padding: 30px 20px;
height: 960px;
}
.download-box {
width: 100%;
// padding: 30px 20px;
outline: 1px solid rgb(17, 0, 255);
background-color: #fff;
overflow: hidden;
.line-box {
.flex-type(flex-start);
height: 30px;
}
&.txt {
background: #282c34;
color: #fff;
height: 900px;
overflow: auto;
}
}
</style>
替代方案
上面是自己寫的,github上面還有好多插件可以用,但各有優(yōu)劣,根據(jù)自己需求選擇
如:
https://github.com/Akryum/vue-virtual-scroller/tree/0f2e36248421ad69f41c9a08b8dcf7839527b8c2
vue-virt-list
vue-draggable-virtual-scroll-list
virtual-list
自己找吧,我就不一一列舉了,看圖

<template>
<br />
<div>
<Table
:columns="tableConfig.columns"
:data="tableConfig.totalData"
:loading="tableConfig.loading"
:pagination="false"
></Table>
</div>
<br />
<div class="button-box">
<a-select
v-model:value="selection"
placeholder="請選擇序列"
:options="seqOptions"
@change="
(selection:string) => handleChangeSeq(baseData.taskId, baseData.barcodeName, width, selection)
"
></a-select>
<a-button type="primary" @click="handleClickExport()">導(dǎo)出所有序列</a-button>
<a-button type="primary" @click="modalConfig.visible = true">導(dǎo)出當前序列</a-button>
</div>
<!-- <SeqText :collapsed="props.collapsed" :seqText="data.geneTexts" /> -->
<div class="enn">
<div class="download-box txt" id="scrollable-div" @scroll="handleScroll">
<div id="inner">
<div v-for="(item, index) in data2" :key="index" class="line-box">
<template v-if="index === 0 && start === 0">
<div :class="{ 'text-title': props.collapsed, 'text-title-samll': !props.collapsed }">
{{ item }}
</div>
</template>
<template v-else>
<div :class="{ 'text-number': props.collapsed, 'text-number-samll': !props.collapsed }">
{{ calLine(item, index + start) }}
</div>
<div :class="{ 'text-box': props.collapsed, 'text-box-samll': !props.collapsed }">
{{ item }}
</div>
<div
:class="{ 'text-number2': props.collapsed, 'text-number2-samll': !props.collapsed }"
>
{{ endRow(item, index + start) }}
</div>
</template>
</div>
</div>
</div>
</div>
<br />
<a-modal
title="導(dǎo)出文件"
:visible="modalConfig.visible"
@ok="handleExport(data.geneTexts)"
@cancel="modalConfig.visible = false"
>
<div class="form-box">
<a-form>
<a-form-item label="自定義文件名">
<a-input v-model:value="modalConfig.name" placeholder="請輸入自定義文件名"></a-input>
</a-form-item>
</a-form>
</div>
</a-modal>
</template>
<script lang="ts" setup>
import { defineComponent, onMounted, PropType, ref } from 'vue';
import Table from '/@/components/table/sTable.vue';
import SeqText from '/@/components/SeqText/index.vue';
import { useText, useTable } from './hooks/useText';
import { useModal } from './hooks/useModal';
import { serverAddress } from '/@/serve/index';
import { download, downloadTxt } from '/@/libs/utils/download';
const props = defineProps({
/**
* 基礎(chǔ)數(shù)據(jù)
*/
baseData: {
type: Object as PropType<{
taskId: string;
barcodeName: string;
}>,
default: {},
},
collapsed: {
type: Boolean,
default: true,
},
type: {
type: Boolean,
default: false,
},
});
let width = 100;
const { taskId, barcodeName } = props.baseData;
const { data, getMedaka, getAvailableSeq, handleChangeSeq, seqOptions, selection } = useText(
props.type,
);
const { tableConfig, getTable } = useTable(props.type);
const VITE_APP_URL = serverAddress();
const { modalConfig, handleExport } = useModal();
const handleClickExport = () => {
let path = '';
if (props.type) {
path = VITE_APP_URL + `outputs/${taskId}/fastq_analysis/${barcodeName}/ragtag.fasta`;
} else {
path =
VITE_APP_URL + `outputs/${taskId}/fastq_analysis/${barcodeName}/${barcodeName}.final.fasta`;
}
download(path, '.fasta');
};
const calLine = (item: any, index: number) => {
return width * (index - 1) + 1;
};
const endRow = (item: any, index: number) => {
return width * index;
};
onMounted(() => {
getAvailableSeq(taskId, barcodeName).then(() => {
if (seqOptions.value.length > 0) {
getMedaka(taskId, barcodeName, width, seqOptions.value[0].value).then(() => init());
// getMedaka(taskId, barcodeName, width);
}
});
getTable(taskId, barcodeName);
});
/**
* 虛擬列表滾動大致思路:兩個div容器
*
* 外層:外部容器用來固定列表容器的高度,同時生成滾動條
*
* 內(nèi)層:內(nèi)部容器用來裝元素,高度是所有元素高度的和
*
* 外層容器鼠標滾動事件 dom.scrollTop 獲取滾動條的位置
*
* 根據(jù)每行列表的高以及當前滾動條的位置,利用slice() 去截取當前需要顯示的內(nèi)容
*
* 重點:滾動條的高度是有內(nèi)層容器的paddingBottom 和 paddingTop 屬性頂起來了,確保滾動條位置的準確性
*
* 這里鼠標上下滾動會出現(xiàn)閃屏問題:解決方案如下:
*
* 方案一: 預(yù)加載:
*
* 向下預(yù)加載:
* 比如div滾動區(qū)域顯示30行,就預(yù)加載 300行( 即這里 slice(startIndex,startIndex + 300) ),
*
* 向上預(yù)加載:
* 在滾動監(jiān)聽事件函數(shù)中(computeRow)判斷inner的paddingTop和paddingBottom即可
*
* 當然這里的download-box的padding有30px像素,在加一個div,overflow:hidded就解決了
*
* 方案二:縮小滾動范圍或者節(jié)流時間縮短,這里寫的500ms
*
*
*/
let timer_throttle: any;
const throttle = (func: Function, wait?: number) => {
wait = wait || 500;
if (!timer_throttle) {
timer_throttle = setTimeout(() => {
func.apply(this);
timer_throttle = null;
}, wait);
}
};
let start = ref(0);
// 鼠標滾動事件
const handleScroll = (event: any) => throttle(computeRow, 100);
// 計算當前顯示tab
const computeRow = () => {
// console.log('距離頂部距離', window.scrollY, geneTexts);
let scrollableDiv = document.getElementById('scrollable-div');
let topPosition = scrollableDiv.scrollTop;
let leftPosition = scrollableDiv.scrollLeft;
console.log('垂直滾動位置:', topPosition, '水平滾動位置:', leftPosition);
const startIndex = Math.max(0, Math.floor(topPosition / 30));
start.value = startIndex;
const endIndex = startIndex + 300;
data2.value = data.geneTexts.slice(startIndex, endIndex);
let inner = document.getElementById('inner');
if (topPosition < 2700) {
// 向上預(yù)計加載,這里判斷了三個高度,可以多判斷幾個,增加流暢度
inner.style.paddingTop = topPosition + 'px';
inner.style.paddingBottom = (data.geneTexts.length + 2) * 30 - topPosition + 'px';
} else if (topPosition + data2.value.length * 30 >= data.geneTexts.length * 30) {
// 這里 9000 是 內(nèi)層div的高度 30 * 300
inner.style.paddingTop = topPosition - 900 + 'px'; //900 是div的高度
inner.style.paddingBottom = 0 + 'px';
} else {
inner.style.paddingTop = topPosition - 2700 + 'px';
inner.style.paddingBottom = (data.geneTexts.length + 2) * 30 + 2700 - topPosition + 'px';
}
};
const data2 = ref([]);
const init = () => {
data2.value = data.geneTexts.slice(0, 300);
let inner = document.getElementById('inner');
inner.style.paddingTop = 0 + 'px';
inner.style.paddingBottom = (data.geneTexts.length + 2) * 30 - 900 + 'px';
};
</script>
<style lang="less" scoped>
// @import '../../../../assets/styles/views/medaka.less';
.button-box {
margin-bottom: 25px;
.flex-type(flex-end);
:deep(.ant-btn) {
margin-left: 10px;
}
}
.enn {
background: #282c34;
outline: 1px solid red;
padding: 30px 20px;
height: 960px;
}
.download-box {
width: 100%;
// padding: 30px 20px;
outline: 1px solid rgb(17, 0, 255);
background-color: #fff;
overflow: hidden;
.line-box {
.flex-type(flex-start);
height: 30px;
}
&.txt {
background: #282c34;
color: #fff;
height: 900px;
overflow: auto;
.el-row {
display: flex;
align-items: center;
margin-bottom: 10px;
margin: auto;
font-size: 22px;
}
}
}
.form-box {
.flex-type(center);
}
:deep(.ant-select-selector) {
min-width: 120px;
}
@media screen and (min-width: 1842px) {
.text-box-samll {
letter-spacing: 1.5px;
font-size: 15px;
}
.text-number-samll {
min-width: 60px;
font-size: 15px;
}
.text-number2-samll {
margin-left: 20px;
min-width: 60px;
font-size: 15px;
}
.text-title-samll {
font-size: 15px;
}
.text-box {
font-size: 22px;
// letter-spacing: 3px;
}
.text-number {
min-width: 100px;
font-size: 22px;
}
.text-number2 {
margin-left: 20px;
min-width: 100px;
font-size: 22px;
}
.text-title {
font-size: 22px;
}
}
@media screen and (min-width: 1600px) and (max-width: 1841px) {
.text-box-samll {
font-size: 15px;
}
.text-number-samll {
min-width: 40px;
font-size: 15px;
}
.text-number2-samll {
margin-left: 20px;
min-width: 40px;
font-size: 15px;
}
.text-title-samll {
font-size: 15px;
}
.text-box {
font-size: 20px;
// letter-spacing: 1.2px;
}
.text-number {
min-width: 60px;
font-size: 15px;
}
.text-number2 {
margin-left: 20px;
min-width: 60px;
font-size: 20px;
}
.text-title {
font-size: 20px;
}
}
@media screen and (min-width: 1443px) and (max-width: 1599px) {
.text-box-samll {
font-size: 13px;
}
.text-number-samll {
min-width: 40px;
font-size: 13px;
}
.text-number2-samll {
margin-left: 20px;
min-width: 40px;
font-size: 13px;
}
.text-title-samll {
font-size: 13px;
}
.text-box {
font-size: 18px;
// letter-spacing: 1.2px;
}
.text-number {
min-width: 60px;
font-size: 15px;
}
.text-number2 {
margin-left: 20px;
min-width: 60px;
font-size: 18px;
}
.text-title {
font-size: 18px;
}
}
@media screen and (max-width: 1442px) {
.text-box-samll {
font-size: 11px;
}
.text-number-samll {
min-width: 40px;
font-size: 11px;
}
.text-number2-samll {
margin-left: 20px;
min-width: 40px;
font-size: 11px;
}
.text-title-samll {
font-size: 11px;
}
.text-box {
font-size: 16px;
// letter-spacing: 1.2px;
}
.text-number {
min-width: 60px;
font-size: 15px;
}
.text-number2 {
margin-left: 20px;
min-width: 60px;
font-size: 16px;
}
.text-title {
font-size: 16px;
}
}
</style>總結(jié)
到此這篇關(guān)于前端虛擬滾動列表(vue虛擬列表)的文章就介紹到這了,更多相關(guān)前端虛擬滾動列表內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue中使用Lodop插件實現(xiàn)打印功能的簡單方法
這篇文章主要給大家介紹了關(guān)于Vue中使用Lodop插件實現(xiàn)打印功能的簡單方法,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Vue具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧2019-12-12
vue?Proxy數(shù)據(jù)代理進行校驗部分源碼實例解析
Proxy提供了強大的Javascript元編程,有許多功能,包括運算符重載,對象模擬,簡潔而靈活的API創(chuàng)建,對象變化事件,甚至Vue 3背后的內(nèi)部響應(yīng)系統(tǒng)提供動力,這篇文章主要給大家介紹了關(guān)于vue?Proxy數(shù)據(jù)代理進行校驗部分源碼解析的相關(guān)資料,需要的朋友可以參考下2022-01-01
Vue實現(xiàn)Echarts圖表寬高自適應(yīng)的實踐
本文主要介紹了Vue實現(xiàn)Echarts圖表寬高自適應(yīng)的實踐,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11
element?ui中el-form-item的屬性rules的用法示例小結(jié)
這篇文章主要介紹了element?ui中el-form-item的屬性rules的用法,本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友一起看看吧2024-07-07
Vue3?中的?readonly?特性及函數(shù)使用詳解
readonly是Vue3中提供的一個新特性,用于將一個響應(yīng)式對象變成只讀對象,這篇文章主要介紹了Vue3?中的?readonly?特性詳解,需要的朋友可以參考下2023-04-04

