VirtualList 가상 목록
화면에 보이는 창 내부의 항목만 렌더링하며, 고정 높이와 동적 높이 두 가지 모드를 지원합니다 (동적 모드는 사전 계산된 오프셋과 이진 탐색으로 위치를 찾습니다).
고정 높이
고정 높이
0번 항목 · 항목 1
1번 항목 · 항목 2
2번 항목 · 항목 3
3번 항목 · 항목 4
vue
<script setup lang="ts">
const listData = Array.from({ length: 1000 }, (_, i) => `항목 ${i + 1}`)
</script>
<template>
<LhVirtualList :data="listData" :height="200" :item-height="40">
<template #default="{ item, index }">
<div>{{ index }}번 항목 · {{ item }}</div>
</template>
</LhVirtualList>
</template>vue
<script>
export default {
data() {
return {
listData: Array.from({ length: 1000 }, (_, i) => `항목 ${i + 1}`),
}
},
}
</script>
<template>
<LhVirtualList :data="listData" :height="200" :item-height="40">
<template #default="{ item, index }">
<div>{{ index }}번 항목 · {{ item }}</div>
</template>
</LhVirtualList>
</template>동적 높이
itemSize가 각 항목의 높이를 반환합니다 (측정 결과를 기반으로 동적으로 반환할 수 있습니다).
동적 높이
항목 1
항목 2
항목 3
vue
<script setup>
const dynamicSize = (index) => (index % 3 === 0 ? 56 : 36)
</script>
<template>
<LhVirtualList :data="listData" :height="200" :item-size="dynamicSize">
<template #default="{ item, index }">{{ item }}</template>
</LhVirtualList>
</template>vue
<script>
export default {
data() {
return {
listData: Array.from({ length: 1000 }, (_, i) => `항목 ${i + 1}`),
}
},
methods: {
dynamicSize(index) {
return index % 3 === 0 ? 56 : 36
},
},
}
</script>
<template>
<LhVirtualList :data="listData" :height="200" :item-size="dynamicSize">
<template #default="{ item, index }">{{ item }}</template>
</LhVirtualList>
</template>사전 렌더링 버퍼
overscan은 화면 창 밖의 상하에 각각 몇 줄을 미리 렌더링할지 지정하여, 빠른 스크롤 시 빈 화면이 보이는 것을 줄입니다.
overscan
0번 항목 · 항목 1
1번 항목 · 항목 2
2번 항목 · 항목 3
3번 항목 · 항목 4
4번 항목 · 항목 5
5번 항목 · 항목 6
6번 항목 · 항목 7
7번 항목 · 항목 8
8번 항목 · 항목 9
9번 항목 · 항목 10
10번 항목 · 항목 11
11번 항목 · 항목 12
12번 항목 · 항목 13
13번 항목 · 항목 14
14번 항목 · 항목 15
15번 항목 · 항목 16
vue
<script setup lang="ts">
const listData = Array.from({ length: 1000 }, (_, i) => `항목 ${i + 1}`)
</script>
<template>
<LhVirtualList
:data="listData"
:item-height="40"
:height="200"
:overscan="8"
/>
</template>vue
<script>
export default {
data() {
return {
listData: Array.from({ length: 1000 }, (_, i) => `항목 ${i + 1}`),
}
},
}
</script>
<template>
<LhVirtualList
:data="listData"
:item-height="40"
:height="200"
:overscan="8"
/>
</template>API
LhVirtualList Props
| 속성명 | 설명 | 타입 | 기본값 |
|---|---|---|---|
| data | 데이터 소스 (항목 렌더링은 default 슬롯이 담당) | any[] | — |
| height | 컨테이너 높이 (숫자는 px) | string | number | — |
| itemHeight | 고정 높이 모드의 항목 높이 | number | — |
| itemSize | 동적 높이 모드: 호출자가 각 항목의 높이를 반환 | (index: number) => number | — |
| overscan | 오버스캔 항목 수 | number | LH_VIRTUAL_OVERSCAN |
| sx | sx 빠른 레이아웃 속성 | LhSxProps | — |
슬롯: default (스코프 { item, index }).
접근성
- 컨테이너는
overflow: auto로 키보드 스크롤이 가능합니다. 창 밖의 항목은 DOM에 존재하지 않으며, 스크린 리더는 필요에 따라 가져옵니다 (긴 목록에서는 본질적인 트레이드오프이므로, 검색/필터로 범위를 좁히는 것을 권장합니다) - 완전한 시맨틱 표가 필요하다면 Table을 사용하세요 (Table의 가상 스크롤 역시 core 순수 함수로 구동됩니다)
타입 정의
이 컴포넌트가 외부로 내보내는 TypeScript 타입 구조는 다음과 같습니다 (packages/components/virtual-list/src/types.ts와 @lh-ui/share/core/* 소스에서 자동 추출되었습니다. 각 항목을 클릭하여 펼쳐 보세요. 속성 설명은 위쪽 Props 표를 참고하세요):
LhVirtualListProps
ts
export interface LhVirtualListProps {
/** 데이터 소스 (항목 렌더링은 default 슬롯이 담당) */
data: any[]
/** 컨테이너 높이 (숫자는 px) */
height: number | string
/** 고정 높이 모드의 항목 높이 (itemSize와 둘 중 하나를 사용하며, itemSize를 제공하면 동적 높이로 동작) */
itemHeight?: number
/** 동적 높이 모드: 호출자가 각 항목의 높이를 반환 */
itemSize?: (index: number) => number
/** 오버스캔 항목 수 */
overscan?: number
/** sx 빠른 레이아웃 속성 */
sx?: LhSxProps
}상수
이 컴포넌트에서 사용하는 공개 상수는 다음과 같습니다 (Props 표의 「타입 / 기본값」 열에서 동일한 이름의 항목을 클릭하면 이곳으로 이동합니다):
LH_VIRTUAL_ITEM_SIZE (@lh-ui/share/core/virtual-list에서 제공)
ts
/** 기본 추정 항목 높이 */
export const LH_VIRTUAL_ITEM_SIZE = 40