Vue 3虚拟滚动单元测试核心是验证可视范围计算、子项渲染数量、偏移量与占位高度,而非模拟滚动动画;需重点测试数据切片、scrollTop响应、transform偏移、占位高度及v-for项数。

在 Vue 3 单元测试中模拟虚拟滚动行为,核心不是“模拟滚动动画”或“复现浏览器滚动引擎”,而是验证组件是否正确计算可视范围、是否只渲染预期子项、是否正确更新偏移量与占位高度。因为虚拟滚动的本质是逻辑计算 + DOM 裁剪,而非真实滚动性能本身。测试重点应落在:数据切片逻辑、scrollTop 响应、transform 偏移、占位容器高度、以及 v-for 渲染项数量。
以下为可落地的测试策略和代码示例(基于 Vitest + Vue Test Utils v2):
✅ 验证可视区域数据切片是否准确
组件内部通常通过 computed 计算 visibleItems 和 offsetY。测试时可直接访问该计算属性(需启用 global.config.renderStubDefaultSlot = false):
import { mount } from '@vue/test-utils'
import VirtualList from '@/components/VirtualList.vue'
test('visibleItems correctly slices items based on scrollTop and itemHeight', () => {
const items = Array.from({ length: 100 }, (_, i) => ({ id: i, name: `Item ${i}` }))
const wrapper = mount(VirtualList, {
props: {
items,
itemHeight: 40,
height: 200 // 容器高 200px → 理论可见约 5 条 + overscan
}
})
// 手动设置 scrollTop(触发响应式更新)
wrapper.vm.scrollTop = 80 // 滚动过前 2 条(80 / 40 = 2)
wrapper.vm.$forceUpdate() // 或 await nextTick()
expect(wrapper.vm.visibleItems).toHaveLength(7) // 含上下缓冲区
expect(wrapper.vm.visibleItems[0].id).toBe(2)
})
⚠️ 注意:若
visibleItems是computed,修改scrollTop后需await nextTick()或wrapper.vm.$forceUpdate()触发重计算。
✅ 模拟滚动事件并断言 DOM 行为
直接触发原生 scroll 事件,并检查关键 DOM 属性:
test('scroll event updates translateY and placeholder height', async () => {
const wrapper = mount(VirtualList, {
props: { items: Array(500).fill(0).map((_, i) => ({ id: i })), itemHeight: 42, height: 300 }
})
const container = wrapper.find('.virtual-scroll-container')
await container.trigger('scroll', { target: { scrollTop: 126 } }) // 滚动 3 行
const content = wrapper.find('.virtual-scroll-content')
expect(content.attributes('style')).toContain('translateY(126px)')
const placeholder = wrapper.find('.virtual-scroll-placeholder')
expect(placeholder.attributes('style')).toContain('height: 21000px') // 500 × 42
})
✅ 提示:确保组件模板中
.virtual-scroll-container有@scroll绑定,且scrollTop由ref或reactive管理,才能被事件触发更新。
Springboot Vue Builder下载从零开始编排 SpringBoot+Vue 全栈项目工作流,适用于新建全栈项目或完整脚手架请求,触发后依次调用 product‑spec‑builder(需求调研)等模块。
✅ 测试动态高度场景(需 mock DOM 尺寸读取)
当列表项高度不固定时,组件常依赖 ResizeObserver 或 ref.offsetHeight。单元测试中无法真实测量,需 mock ref 的 DOM 节点高度:
test('dynamic height list computes correct accumulated offsets', async () => {
const items = [{ id: 0 }, { id: 1 }, { id: 2 }]
const wrapper = mount(VirtualList, {
props: { items, height: 200 }
})
// 手动 mock 每个 item ref 的 offsetHeight(在 setup 中通常用 onMounted + ref 回调收集)
const itemRefs = [
{ offsetHeight: 32 },
{ offsetHeight: 58 },
{ offsetHeight: 44 }
]
// 假设组件暴露了内部高度缓存用于测试(如 via wrapper.vm.itemHeights)
wrapper.vm.itemHeights.set('0', 32)
wrapper.vm.itemHeights.set('1', 58)
wrapper.vm.itemHeights.set('2', 44)
await nextTick()
expect(wrapper.vm.accumulatedHeights).toEqual([32, 90, 134]) // 前缀和
})
? 建议:在组件中将高度缓存(如
itemHeights: Map)设为expose或提供getAccumulatedHeights()方法,便于测试断言。
❌ 不推荐的做法
- 用
jest.useFakeTimers()模拟滚动节流 —— 虚拟滚动一般不依赖定时器,逻辑在 scroll 同步触发; - 尝试“滚动到某位置并截图比对” —— E2E 范畴,非单元测试职责;
- 断言
window.scrollY—— 虚拟滚动操作的是容器内scrollTop,与页面滚动无关。
真实项目中,建议为虚拟列表组件导出纯函数工具(如 calculateVisibleRange(scrollTop, itemHeight, containerHeight, overscan, itemCount)),单独对它做 Jest 单元测试,隔离 DOM 依赖,覆盖边界 case(如 scrollTop 、scrollTop > totalHeight、空数据等)。
这样既轻量,又保证核心算法 100% 可测、可维护。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











