ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

@tanstack/vue-virtual 版本演进解析:从 3.13.3 到 3.13.39 的核心修复与底层原理

@tanstack/vue-virtual 版本演进解析:从 3.13.3 到 3.13.39 的核心修复与底层原理 前端UI组件【免费下载链接】virtual Headless UI for Virtualizing Large Element Lists in JS/TS, React, Solid, Vue and Svelte项目地址https://gitcode.com/gh_mirrors/vi/virtual点击查看免费下载tanstack/vue-virtual是 TanStack Virtual 在 Vue 框架上的官方适配层负责把无框架的tanstack/virtual-core核心虚拟化逻辑封装为响应式的 Vue 组合式 API。本文以仓库中的 CHANGELOG 为主线梳理该适配器从 3.13.3 到 3.13.39 的版本演进脉络结合 源码实现、虚拟化核心 API 文档 与 Vue 示例 中的真实代码讲清楚每个关键修复背后的原理以及 Vue 适配器究竟如何把核心虚拟化能力桥接到 Vue 的响应式体系。读完你会掌握如何阅读这个包的版本记录并追踪底层 core 的变更、useVirtualizer/useWindowVirtualizer两个入口的响应式工作原理以及动态测量、锚定模式等新能力的实战用法。版本总览Vue 适配器版本节奏与 core 的耦合关系tanstack/vue-virtual走的是「薄适配层 共享核心」的包结构。从 CHANGELOG 可以看到一个非常清晰的事实几乎所有版本号3.13.4 ~ 3.13.39都只有一个 Patch Changes 条目即「Updated dependencies」指向tanstack/virtual-core的某个新版本。这并非版本记录偷懒而是工程设计的必然结果。查看包的依赖声明package.json 中唯一的运行时依赖就是tanstack/virtual-core以 workspace 协议引入整个src/index.ts不过百来行。也就是说3.13.4 ~ 3.13.12、3.13.14 ~ 3.13.21、3.13.23 ~ 3.13.26 等版本纯属 core 升级适配器自身零改动3.13.13 是唯一一个自带 Fix 条目的版本修复了count变化时getTotalSize()返回过期值的问题3.13.22、3.13.27、3.13.28、3.13.39 等版本对应 core 的 minor如 3.14.0、3.15.0、3.16.0、3.17.0/3.17.11或 patch 升级其中 core 的 3.16.0 与 3.17.0 引入了影响面较大的新能力。在仓库中可以通过两条 CHANGELOG 对照阅读Vue 侧记录「适配器自身改了什么 依赖的 core 升到哪」virtual-core 的 CHANGELOG 记录「core 具体修了什么」。这种分层让框架适配器与核心逻辑可以各自独立迭代、共享同一份性能与正确性改进这也是本仓库所有框架包React、Solid、Svelte、Lit、Marko、Angular 等共用的架构。版本对照速查表下表整理了本仓库所记录的两个包在对应版本号上的耦合关系Vue 适配器 3.13.39 对应 virtual-core 3.17.11为当前最新版本tanstack/vue-virtualtanstack/virtual-core主要变更3.13.33.13.3基础版本3.13.133.13.13修复 count 变化时 getTotalSize() 过期适配器层唯一 Fix3.13.243.14.0core minor 升级3.13.253.15.0core minor 升级多列 masonry 相关能力3.13.273.16.0/3.16.1core minoranchorTo: end 聊天/日志模式等3.13.283.17.0core minoruseCachedMeasurements 选项3.13.393.17.11最新版本core 修复平滑滚动、锚定补偿等唯一自带修复的版本3.13.13 与 count 变化时的高度更新在 CHANGELOG 的 3.13.13 条目中记录了一个值得展开的修复对应 upstream PR #1085Fix: Notify framework when count changes to updategetTotalSize()问题现象当count选项发生变化时例如前端做过滤或搜索列表从 100 条变成 20 条getTotalSize()会返回过期值。修复前过滤后列表容器仍保持之前的高度——count减少时出现大片空白count增加时新内容不可达。修复方式virtualizer 在「会影响测量结果的选项」变化时自动通知框架。也就是说core 现在会追踪哪些选项影响测量缓存一旦count这类选项变更就触发框架侧重新渲染让高度随count同步更新用户不再需要手写useMemo之类的补偿逻辑。条目还强调该修复对所有框架适配器生效且每次变化的性能开销极小 0.1ms。对照 Vue 适配器的实现可以理解「通知框架」是如何落地的。在 packages/vue-virtual/src/index.ts 中适配器用watch监听选项对象一旦变化就调用virtualizer.setOptions(...)并在onChange回调里执行triggerRef(state)watch( () unref(options), (options) { virtualizer.setOptions({ ...options, onChange: (instance, sync) { triggerRef(state) options.onChange?.(instance, sync) }, }) virtualizer._willUpdate() triggerRef(state) }, { immediate: true }, )state是一个shallowRef(virtualizer)triggerRef强制触发该 ref 的更新从而让依赖它的getTotalSize()/getVirtualItems()计算属性重新求值。这就是「core 通知框架」在 Vue 侧的完整链路core 内部检测到测量相关选项变化 → 调用onChange→ 适配器triggerRef→ 模板中的computed重新计算。适配器的响应式工作原理useVirtualizer 与 useWindowVirtualizer两个组合式 API 的入口Vue 框架文档 明确说明tanstack/vue-virtual是围绕核心虚拟逻辑的薄封装对外只暴露两个函数function useVirtualizerTScrollElement, TItemElement unknown( options: PartialKeys VirtualizerOptionsTScrollElement, TItemElement, observeElementRect | observeElementOffset | scrollToFn , ): VirtualizerTScrollElement, TItemElement function useWindowVirtualizerTItemElement unknown( options: PartialKeys VirtualizerOptionsWindow, TItemElement, | getScrollElement | observeElementRect | observeElementOffset | scrollToFn , ): VirtualizerWindow, TItemElement两者的区别只在于滚动载体useVirtualizer返回配置为以HTML 元素作为滚动元素的Virtualizer实例useWindowVirtualizer返回以window作为滚动元素的实例用于整页滚动场景。源码级实现解析从 packages/vue-virtual/src/index.ts 可以完整还原适配器实现。两个入口都汇聚到私有的useVirtualizerBase创建实例const virtualizer new Virtualizer(unref(options))用shallowRef包裹为state挂载清理调用virtualizer._didMount()得到cleanup注册到onScopeDispose(cleanup)组件销毁时自动解除 ResizeObserver、滚动监听等副作用滚动元素监听watch(() unref(options).getScrollElement(), ...)滚动元素一旦就绪比如 ref 绑定完成就调用_willUpdate()选项响应式同步上面的watch用setOptions把新选项含包装后的onChange同步给 core并触发_willUpdate()与triggerRef(state)返回state即RefVirtualizer模板中通过.value访问实例方法。useVirtualizer额外通过computed注入三个默认实现useVirtualizerBase( computed(() ({ observeElementRect: observeElementRect, observeElementOffset: observeElementOffset, scrollToFn: elementScroll, ...unref(options), })), )即元素模式下默认使用observeElementRect/observeElementOffset基于 ResizeObserver 与 scroll 事件和elementScroll。useWindowVirtualizer则注入getScrollElement: () window、observeWindowRect、observeWindowOffset、windowScroll以及initialOffset: () window.scrollY见 源码这些默认实现与 virtualizer API 文档 中描述的elementScroll/windowScroll/observeElementRect/observeWindowRect一一对应。需要注意尽管文档签名写作返回Virtualizer实际实现返回的是RefVirtualizerstateVue 模板与计算属性中要用.value访问——这正是 示例代码 中rowVirtualizer.value.getVirtualItems()的写法来源。版本演进中的核心能力从 core 升级看功能增量虽然 Vue 适配器自身改动极少但跟随 core 的版本升级Vue 用户也同步获得了大量底层能力。以下是本仓库 virtual-core CHANGELOG 中记录的、随 Vue 适配器各版本一起落地的关键能力core 3.16.0anchorTo: end 聊天/日志模式core 3.16.0对应 Vue 3.13.26 → 3.13.27引入了端锚定虚拟化专为聊天、日志、反向信息流设计新增anchorTo: end选项当旧内容被前插prepend时保持当前可见项稳定流式输出中最后一项增长时保持视口钉在底部默认仍是start顶部/左侧锚定保持原有行为新增followOnAppend只有视口原本就在末尾时新追加内容才自动滚入视野往上翻看历史的用户不会被拉回底部新增辅助 APIscrollEndThreshold、scrollToEnd()、getDistanceFromEnd()、isAtEnd()。这些 API 的语义在 virtualizer 文档 中有完整定义scrollEndThreshold默认1像素阈值isAtEnd(threshold?)判断视口是否在距末端阈值范围内scrollToEnd()对纵向列表滚动到底部。配套文档还强调前插稳定性要求基于持久 id 的稳定getItemKey因为索引键无法区分前插与追加。3.16.1 又修复了一个前插时的「一帧跳跃」问题anchorTo: end下前插内容时会有一帧按旧估算位置计算可见范围随后_willUpdate修正产生可见跳动修复后在渲染过程中于setOptions内提前调整scrollOffset使calculateRange/getVirtualItems立即返回正确条目。core 3.17.0useCachedMeasurements 与测量缓存core 3.17.0对应 Vue 3.13.28新增useCachedMeasurements选项见 virtualizer 文档启用后默认measureElement跳过 DOM 读取直接返回缓存尺寸无缓存则回退到estimateSize典型场景列表被临时隐藏如父元素display: none时ResizeObserver 会对所有项报告尺寸 0导致测量被重置启用该选项后隐藏期间测量不被清零恢复显示后也不会出现布局跳动使用方式是在隐藏前把该选项置true、显示后置falseResizeObserver 始终保持挂载关闭后真实测量自动恢复注意它只影响默认measureElement自定义测量时需自行处理。3.17.0 还顺带优化了默认measureElement已有缓存时跳过同步 DOM 读offsetWidth/offsetHeight减少重渲染时的 layout reflow。滚动与测量正确性修复3.17.x 系列从 3.17.1 到 3.17.11 的密集 patch 主要打磨滚动补偿与测量时序这些修复全部随 Vue 适配器 3.13.29 自动获得向上滚动不跳动3.17.1默认滚动补偿谓词在向上滚动时也补偿「估算→实测」首测差值但跳过重测补偿避免级联抖动滚动方向不误锁3.17.3、3.17.5虚拟器自身补偿写入触发的滚动事件不再被当作backward方向锁定避免多帧回流期间视口漂移减少 GC 压力3.17.3默认单车道路径按滚动帧零分配去掉每次滚动事件上的选项对象与闭包分配gap 选项变化失效测量3.17.4gap 变更会失效测量缓存多车道masonry布局改用增量车道 argmin替代反向扫描滚动事件去重与端锚定同步3.17.2跳过相同 offset 的冗余滚动事件applyScrollAdjustment中同步scrollOffset避免端锚定流式增长时被浏览器 clamp 丢失iOS 处理3.17.5、3.17.6、3.17.7清理时重置 iOS 手势/延迟状态视口整体跨越折叠线的条目增长不再默认补偿避免聊天流式消息被逐 token 拖拽iOS 延迟补偿不再重放过期增量平滑滚动存活3.17.11前插内容时保持行进中的平滑scrollToIndex存活anchorTo: end下不再被同步写scrollTop打断debounced 滚动结束回退读取当前 offset避免被过期状态覆盖。这些条目同样值得开发者关注如果你的 Vue 列表在聊天、日志、流式输出、iOS 触屏滚动等场景遇到跳动、漂移或钉底失效问题对应的修复版本就是排查与升级依据。Vue 中的实际用法从仓库示例看标准接线固定/动态尺寸的经典写法examples/vue/fixed 展示了基于固定尺寸的「行、列、网格」三种形态examples/vue/variable 展示了动态尺寸写法。核心接线方式以动态为例script setup langts import { ref, computed } from vue import { useVirtualizer } from tanstack/vue-virtual const parentRef refHTMLElement | null(null) const rowVirtualizer useVirtualizer({ count: props.rows.length, getScrollElement: () parentRef.value, estimateSize: (i) props.rows[i], overscan: 5, }) const virtualRows computed(() rowVirtualizer.value.getVirtualItems()) const totalSize computed(() rowVirtualizer.value.getTotalSize()) /script template div refparentRef classList styleheight: 200px; overflow: auto div :style{ height: ${totalSize}px, position: relative } div v-forvirtualRow in virtualRows :keyvirtualRow.index :style{ position: absolute, top: 0, left: 0, width: 100%, height: ${virtualRow.size}px, transform: translateY(${virtualRow.start}px), } Row {{ virtualRow.index }} /div /div /div /template要点拆解getScrollElement: () parentRef.value返回滚动容器配合适配器内部的watch实现滚动元素的响应式绑定外层容器高度设为totalSize撑起整个滚动区域每个虚拟项position: absolute; top: 0加transform: translateY(start px)绝对定位到对应位置需要动态测量时variable 场景给元素加:refrowVirtualizer.value.measureElement与data-index虚拟器会用 ResizeObserver 实测尺寸并逐步逼近真实高度。无限滚动与 Vue Query 组合examples/vue/infinite-scroll/src/App.vue 展示了无限滚动的完整模式useInfiniteQuery分页拉数据 →allRows合并所有页 →useVirtualizer接收computed选项count: hasNextPage ? allRows.length 1 : allRows.length→ 用一个额外的 loader 行占位。关键触发逻辑用watchEffect实现当可见项中最后一项接近数据末尾且hasNextPage为真时调用fetchNextPage()watchEffect(() { const [lastItem] [...virtualRows.value].reverse() if (!lastItem) return if ( lastItem.index allRows.value.length - 1 hasNextPage.value !isFetchingNextPage.value ) { fetchNextPage() } })这个示例还体现了两个适配器特性选项用computed传入useVirtualizer(rowVirtualizerOptions)当分页数据增长时count变化适配器会通过watchsetOptions把新选项同步给 core——这正是 3.13.13 修复所保证的「count 变化后高度自动更新」在真实场景中的应用。版本对照与升级建议结合本仓库两条 CHANGELOG 可以给出以下实用的版本追踪方法看 Vue 包版本号tanstack/vue-virtual的 3.13.x 序列几乎全部对应tanstack/virtual-core的 3.13.x ~ 3.17.x只有 3.13.13 是适配器自身修复追 core 的 minor想要新能力聊天锚定、缓存测量、多车道优化看 core 的 3.14.0、3.15.0、3.16.0、3.17.0 各自引入什么再映射到对应的 Vue 版本3.13.24、3.13.25、3.13.27、3.13.28关注滚动正确性修复3.17.x 系列密集修复了 iOS、平滑滚动、锚定补偿等边界问题如果你的场景命中这些边界优先升级到较新的 3.13.39包结构与构建信息tanstack/vue-virtual以 ESM/CJS 双格式发布见 package.json 的exports字段peerDependencies支持 Vue^2.7.0 || ^3.0.0sideEffects: false便于 tree-shaking。升级时建议参照仓库的 workspace 结构Vue 适配器与 core 在同一仓库内协同发版pnpm-lock.yaml与pnpm-workspace.yaml锁定了版本关系本地开发可通过packages/vue-virtual目录结合examples/vue下的固定、动态、无限滚动、padding、scroll-padding、smooth-scroll、sticky、table 等示例进行验证。文档侧Vue 框架指南 提供组合式 API 的类型签名Virtualizer API 文档 提供全部选项与实例方法详解两者配合 CHANGELOG 阅读即可获得完整的使用与演进视图。赞分享前端UI组件【免费下载链接】virtual Headless UI for Virtualizing Large Element Lists in JS/TS, React, Solid, Vue and Svelte项目地址https://gitcode.com/gh_mirrors/vi/virtual点击查看免费下载相关推荐TanStack Solid Query Devtools 演进与实战版本变更、核心原理与配置详解TanStack Solid Query Devtools 演进与实战版本变更、核心原理与配置详解 tanstack/solid query devtool前端缓存状态管理Focalboard 版本演进全解析从 v0.6 到 v0.15 的核心功能与实现原理Focalboard 版本演进全解析从 v0.6 到 v0.15 的核心功能与实现原理 Focalboard 是一个开源、可自托管的项目管理工具定位为 Tr后端前端企业应用桌面应用协同办公从 CHANGELOG 读懂 eggjs/coreEgg 框架核心的版本演进与底层机制从 CHANGELOG 读懂 eggjs/coreEgg 框架核心的版本演进与底层机制 本文以 packages/core/CHANGELOG.md htt后端Web框架上一篇如何 10 分钟跑通 FinRobot多智能体股票研究平台完整上手指南下一篇如何快速构建高性能Web应用Lwan与Lua集成的终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表