ComboboxVirtualizer 虚拟滚动组件:Props、Slots 与 10 万级数据渲染实战)
Radix VueReka UIComboboxVirtualizer 虚拟滚动组件Props、Slots 与 10 万级数据渲染实战【免费下载链接】radix-vueAn open-source UI component library for building high-quality, accessible design systems and web apps for Vue. Previously Radix Vue项目地址: https://gitcode.com/GitHub_Trending/ra/radix-vue导读ComboboxVirtualizer是 Radix Vue现更名为 Reka UICombobox 体系中面向大规模选项列表的虚拟滚动渲染组件它借助tanstack/vue-virtual只挂载视口内的 DOM 节点让数万乃至十万条候选数据也能流畅搜索、选择与键盘导航。本文以其 API 文档docs/content/meta/ComboboxVirtualizer.md为主线逐项拆解全部 Props 与 Slots并结合 源码实现、底层 ListboxVirtualizer 及 测试用例给出可复制的完整示例与常见坑位排查方案。一、组件定位与基本用法1.1 它解决什么问题Combobox 常用于搜索并选择场景但选项量一旦达到数千、数万条一次性渲染全部ComboboxItem会导致首屏卡顿、内存飙升。ComboboxVirtualizer通过虚拟化virtualization技术只渲染当前滚动视口内可见的选项其余选项以占位高度参与滚动计算从而保证渲染性能无论数据总量多大DOM 节点数恒定约视口容量 overscan 冗余内存占用只有可见条目被真实挂载交互体验搜索过滤、键盘导航、type-ahead 仍与普通 Combobox 完全一致。1.2 放在哪里ComboboxVirtualizer必须嵌套在ComboboxViewport内部替代原本直接渲染ComboboxItem的循环。以官方文档中的 10 万条数据示例docs/content/docs/components/combobox.md为基础script setup langts import { ComboboxContent, ComboboxInput, ComboboxItem, ComboboxPortal, ComboboxRoot, ComboboxViewport, ComboboxVirtualizer, useFilter, } from reka-ui import { computed, ref } from vue const people Array.from({ length: 100000 }).map((_, id) ({ id, name: Person #${id} })) const selectedPeople ref(people[0]) const searchTerm ref() const { contains } useFilter({ sensitivity: base }) const filteredPeople computed(() people.filter(p contains(p.name, searchTerm.value))) /script template ComboboxRoot v-modelselectedPeople ComboboxInput v-modelsearchTerm / ComboboxPortal ComboboxContent classmax-h-[40vh] overflow-hidden ComboboxViewport ComboboxVirtualizer v-slot{ option } :optionsfilteredPeople :text-content(x) x.name :estimate-size24 ComboboxItem :valueoption {{ option.name }} /ComboboxItem /ComboboxVirtualizer /ComboboxViewport /ComboboxContent /ComboboxPortal /ComboboxRoot /template注仓库中该库以reka-ui包名发布早期版本即 Radix Vue安装入口见 docs/content/docs/components/combobox.md 的安装说明。二、Props 完整详解下表为ComboboxVirtualizer的全部 Props来源docs/content/meta/ComboboxVirtualizer.mdNameDescriptionTypeRequiredDefaultestimateSizeEstimated size (in px) of each itemnumber \| ((index: number) number)No-optionsList of itemsTYes-overscanNumber of items rendered outside the visible areanumberNo-textContentText content for each item to achieve type-ahead feature((option: T) string)No-2.1options必填类型T泛型实际为T[]——待渲染的完整选项数组即List of items。这是唯一必填项。ComboboxVirtualizerPropsT extends AcceptableValue AcceptableValue在 ComboboxVirtualizer.vue 中定义并完全继承自ListboxVirtualizerPropsTListboxVirtualizer.vue。底层useVirtualizer以props.options.length作为虚拟列表总长度ListboxVirtualizer.vue因此传入过滤后的数组而非原始全量数组配合useFilter即可实现搜索即虚拟化见上例的filteredPeople数组内容变更增删、过滤时虚拟器会自动重算无需手动刷新。2.2estimateSize可选类型number | ((index: number) number)——每项预估高度像素可传固定值或按索引动态计算的函数。源码中的处理逻辑ListboxVirtualizer.vueestimateSize(index) { if (typeof props.estimateSize function) return props.estimateSize(index) return props.estimateSize ?? 28 }要点不传时默认回退到28pxAPI 表显示 Default 为-但源码确认存在 28px 的兜底值所有项高度一致时传固定数字即可官方示例用24或25项高度不固定时传函数按索引返回对应高度估算值应与实际渲染高度尽量一致偏差过大会导致滚动时出现跳动、底部空白或提前加载这是虚拟化滚动质量的关键参数。2.3overscan可选类型number——视口外额外渲染的项数即Number of items rendered outside the visible area。默认值为12见 ListboxVirtualizer.vueoverscan: props.overscan ?? 12。作用滚轮/触控板快速滚动时预渲染视口上下各overscan个条目避免内容闪现白屏值越大滚动越平滑但 DOM 节点随之增多。中等数据量保持默认即可超长列表若追求极致内存可适度调小。2.4textContent可选类型((option: T) string)——为每个选项提供文本内容用于输入字母时自动跳转到匹配项type-ahead。未提供时的回退逻辑ListboxVirtualizer.vueconst parseTextContent (option: T) { if (props.textContent) return props.textContent(option) else return option?.toString().toLowerCase() }若选项是对象如{ name }其toString()会是[object Object]type-ahead 将失效因此对象型选项强烈建议显式传入例如:text-content(x) x.name。该值同时参与search累积匹配ListboxVirtualizer.vue1 秒内连续键入字符refAutoReset(, 1000)会滚动并高亮首个匹配项保证虚拟化列表的键盘可达性。三、Slots 详解ComboboxVirtualizer默认插槽向作用域暴露三个绑定来源docs/content/meta/ComboboxVirtualizer.mdNameDescriptionTypeoption当前渲染项的数据Tvirtualizer底层虚拟器实例VirtualizerHTMLElement, ElementvirtualItem当前虚拟项元数据VirtualItem3.1option当前需要渲染的选项数据T直接用于构造ComboboxItem :valueoption。最常用ComboboxVirtualizer v-slot{ option } :optionsfilteredOptions ComboboxItem :valueoption{{ option.label }}/ComboboxItem /ComboboxVirtualizer3.2virtualizer与virtualItemvirtualizer是tanstack/vue-virtual的VirtualizerHTMLElement, Element实例可用于手动控制如virtualizer.scrollToIndex(index)virtualItem是VirtualItem含index、key、start、size等几何信息可做自定义定位、交错动画或按需数据加载。完整作用域类型在 ComboboxVirtualizer.vue 中声明defineSlots{ default?: (props: { option: T virtualizer: VirtualizerHTMLElement, Element virtualItem: VirtualItem }) any }()四、底层实现原理4.1 一层薄封装复用 Listbox 虚拟器ComboboxVirtualizer本身很薄ComboboxVirtualizer.vue 仅 34 行它在挂载时通过注入的injectComboboxRootContext()将rootContext.isVirtual.value true第 22-24 行随后把全部 Props 与插槽透传给ListboxVirtualizer第 27-33 行。4.2 核心渲染机制ListboxVirtualizer虚拟化引擎useVirtualizer以父元素useParentElement()获取的滚动容器作为滚动源配置count、estimateSize、overscan与水平/垂直方向ListboxVirtualizer.vue绝对定位布局外层容器高度为virtualizer.getTotalSize()总内容高度每个可见项以transform: translateY(${item.start}px)绝对定位摆放并注入data-index、aria-setsize、aria-posinset等语义属性ListboxVirtualizer.vue滚动与高亮联动通过virtualFocusHook/virtualHighlightHook监听选中值与高亮变化调用scrollToIndex把选中项滚动进视口ListboxVirtualizer.vue键盘支持上下键、Home/End、Shift 多选区间替换、MetaA 全选、type-ahead 均经由virtualKeydownHook处理ListboxVirtualizer.vue。4.3 测试验证Combobox.test.ts 用 100 条数据构造VirtualCombobox并断言挂载后[roleoption]数量大于 0 且小于 100——证明虚拟化只渲染子集第 262-271 行点击可见项后data-state变为checked选中状态与普通列表行为一致第 273-288 行。仓库另有针对虚拟化场景的 Story 演示ComboboxVirtual.story.vue展示了带过滤、多选、图标指示器的完整可运行示例。五、通用虚拟化指南与踩坑排查该组件与其他虚拟器ListboxVirtualizer、TreeVirtualizer共用同一套机制通用规范见 docs/content/docs/guides/virtualization.md5.1 三条使用要诀虚拟器外层必须有明确高度给ComboboxViewport设置固定高度或最大高度并允许滚动如max-h-80 overflow-y-auto或max-h-[40vh] overflow-hidden否则useVirtualizer无法获知视口尺寸保证项高一致并正确设置estimateSize高度漂移会造成滚动跳动务必设置textContent否则对象型选项的 type-ahead 不可用影响无障碍体验。5.2 常见问题虚拟化不生效官方指南docs/content/docs/guides/virtualization.md明确指出先检查Virtualizer的父元素是否定义了高度。参考正确写法ComboboxContent !-- Height must be defined -- ComboboxViewport classmax-h-80 overflow-y-auto ComboboxVirtualizer :optionsitems … /ComboboxVirtualizer /ComboboxViewport /ComboboxContent若父容器高度为 0 或auto虚拟器会认为视口不存在表现即为全部渲染或什么都不渲染。六、与相邻 API 的协作配合ComboboxViewport虚拟器必须位于其内部滚动事件由它承载配合ComboboxEmpty过滤无结果时显示空态提示可与虚拟列表并列放置配合ComboboxItemIndicator在虚拟项内部渲染选中对勾实现多选/单选反馈见 ComboboxVirtual.story.vue配合useFilter对options做前置过滤后再传入搜索过程同样保持虚拟化性能。组件从包入口统一导出Combobox/index.ts可直接import { ComboboxVirtualizer } from reka-ui使用其 Props 类型ComboboxVirtualizerProps同时被导出便于二次封装时进行类型推导。结语ComboboxVirtualizer以极小的封装成本为 Combobox 带来了工业级的列表虚拟化能力把握options必填、estimateSize默认 28px、支持动态函数、overscan默认 12、textContent对象选项必配四个 Props 与option/virtualizer/virtualItem三个插槽绑定再满足父容器定高、项高一致两条前置条件即可让数万条候选数据保持与几十条数据相同的流畅交互。其底层复用ListboxVirtualizer并打通了选中滚动、键盘导航与 type-ahead是构建高性能 Combobox 的首选方案。【免费下载链接】radix-vueAn open-source UI component library for building high-quality, accessible design systems and web apps for Vue. Previously Radix Vue项目地址: https://gitcode.com/GitHub_Trending/ra/radix-vue创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考