解析:從 3.13.3 到 3.13.39 的核心修復(fù)與底層原理)
前端UI組件【免費(fèi)下載鏈接】virtual Headless UI for Virtualizing Large Element Lists in JS/TS, React, Solid, Vue and Svelte項(xiàng)目地址https://gitcode.com/gh_mirrors/vi/virtual點(diǎn)擊查看免費(fèi)下載tanstack/vue-virtual是 TanStack Virtual 在 Vue 框架上的官方適配層負(fù)責(zé)把無框架的tanstack/virtual-core核心虛擬化邏輯封裝為響應(yīng)式的 Vue 組合式 API。本文以倉庫中的 CHANGELOG 為主線梳理該適配器從 3.13.3 到 3.13.39 的版本演進(jìn)脈絡(luò)結(jié)合 源碼實(shí)現(xiàn)、虛擬化核心 API 文檔 與 Vue 示例 中的真實(shí)代碼講清楚每個(gè)關(guān)鍵修復(fù)背后的原理以及 Vue 適配器究竟如何把核心虛擬化能力橋接到 Vue 的響應(yīng)式體系。讀完你會(huì)掌握如何閱讀這個(gè)包的版本記錄并追蹤底層 core 的變更、useVirtualizer/useWindowVirtualizer兩個(gè)入口的響應(yīng)式工作原理以及動(dòng)態(tài)測(cè)量、錨定模式等新能力的實(shí)戰(zhàn)用法。版本總覽Vue 適配器版本節(jié)奏與 core 的耦合關(guān)系tanstack/vue-virtual走的是「薄適配層 共享核心」的包結(jié)構(gòu)。從 CHANGELOG 可以看到一個(gè)非常清晰的事實(shí)幾乎所有版本號(hào)3.13.4 ~ 3.13.39都只有一個(gè) Patch Changes 條目即「Updated dependencies」指向tanstack/virtual-core的某個(gè)新版本。這并非版本記錄偷懶而是工程設(shè)計(jì)的必然結(jié)果。查看包的依賴聲明package.json 中唯一的運(yùn)行時(shí)依賴就是tanstack/virtual-core以 workspace 協(xié)議引入整個(gè)src/index.ts不過百來行。也就是說3.13.4 ~ 3.13.12、3.13.14 ~ 3.13.21、3.13.23 ~ 3.13.26 等版本純屬 core 升級(jí)適配器自身零改動(dòng)3.13.13 是唯一一個(gè)自帶 Fix 條目的版本修復(fù)了count變化時(shí)getTotalSize()返回過期值的問題3.13.22、3.13.27、3.13.28、3.13.39 等版本對(duì)應(yīng) core 的 minor如 3.14.0、3.15.0、3.16.0、3.17.0/3.17.11或 patch 升級(jí)其中 core 的 3.16.0 與 3.17.0 引入了影響面較大的新能力。在倉庫中可以通過兩條 CHANGELOG 對(duì)照閱讀Vue 側(cè)記錄「適配器自身改了什么 依賴的 core 升到哪」virtual-core 的 CHANGELOG 記錄「core 具體修了什么」。這種分層讓框架適配器與核心邏輯可以各自獨(dú)立迭代、共享同一份性能與正確性改進(jìn)這也是本倉庫所有框架包React、Solid、Svelte、Lit、Marko、Angular 等共用的架構(gòu)。版本對(duì)照速查表下表整理了本倉庫所記錄的兩個(gè)包在對(duì)應(yīng)版本號(hào)上的耦合關(guān)系Vue 適配器 3.13.39 對(duì)應(yīng) virtual-core 3.17.11為當(dāng)前最新版本tanstack/vue-virtualtanstack/virtual-core主要變更3.13.33.13.3基礎(chǔ)版本3.13.133.13.13修復(fù) count 變化時(shí) getTotalSize() 過期適配器層唯一 Fix3.13.243.14.0core minor 升級(jí)3.13.253.15.0core minor 升級(jí)多列 masonry 相關(guān)能力3.13.273.16.0/3.16.1core minoranchorTo: end 聊天/日志模式等3.13.283.17.0core minoruseCachedMeasurements 選項(xiàng)3.13.393.17.11最新版本core 修復(fù)平滑滾動(dòng)、錨定補(bǔ)償?shù)任ㄒ蛔詭迯?fù)的版本3.13.13 與 count 變化時(shí)的高度更新在 CHANGELOG 的 3.13.13 條目中記錄了一個(gè)值得展開的修復(fù)對(duì)應(yīng) upstream PR #1085Fix: Notify framework when count changes to updategetTotalSize()問題現(xiàn)象當(dāng)count選項(xiàng)發(fā)生變化時(shí)例如前端做過濾或搜索列表從 100 條變成 20 條getTotalSize()會(huì)返回過期值。修復(fù)前過濾后列表容器仍保持之前的高度——count減少時(shí)出現(xiàn)大片空白count增加時(shí)新內(nèi)容不可達(dá)。修復(fù)方式virtualizer 在「會(huì)影響測(cè)量結(jié)果的選項(xiàng)」變化時(shí)自動(dòng)通知框架。也就是說core 現(xiàn)在會(huì)追蹤哪些選項(xiàng)影響測(cè)量緩存一旦count這類選項(xiàng)變更就觸發(fā)框架側(cè)重新渲染讓高度隨count同步更新用戶不再需要手寫useMemo之類的補(bǔ)償邏輯。條目還強(qiáng)調(diào)該修復(fù)對(duì)所有框架適配器生效且每次變化的性能開銷極小 0.1ms。對(duì)照 Vue 適配器的實(shí)現(xiàn)可以理解「通知框架」是如何落地的。在 packages/vue-virtual/src/index.ts 中適配器用watch監(jiān)聽選項(xiàng)對(duì)象一旦變化就調(diào)用virtualizer.setOptions(...)并在onChange回調(diào)里執(zhí)行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是一個(gè)shallowRef(virtualizer)triggerRef強(qiáng)制觸發(fā)該 ref 的更新從而讓依賴它的getTotalSize()/getVirtualItems()計(jì)算屬性重新求值。這就是「core 通知框架」在 Vue 側(cè)的完整鏈路core 內(nèi)部檢測(cè)到測(cè)量相關(guān)選項(xiàng)變化 → 調(diào)用onChange→ 適配器triggerRef→ 模板中的computed重新計(jì)算。適配器的響應(yīng)式工作原理useVirtualizer 與 useWindowVirtualizer兩個(gè)組合式 API 的入口Vue 框架文檔 明確說明tanstack/vue-virtual是圍繞核心虛擬邏輯的薄封裝對(duì)外只暴露兩個(gè)函數(shù)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兩者的區(qū)別只在于滾動(dòng)載體useVirtualizer返回配置為以HTML 元素作為滾動(dòng)元素的Virtualizer實(shí)例useWindowVirtualizer返回以window作為滾動(dòng)元素的實(shí)例用于整頁滾動(dòng)場(chǎng)景。源碼級(jí)實(shí)現(xiàn)解析從 packages/vue-virtual/src/index.ts 可以完整還原適配器實(shí)現(xiàn)。兩個(gè)入口都匯聚到私有的useVirtualizerBase創(chuàng)建實(shí)例const virtualizer new Virtualizer(unref(options))用shallowRef包裹為state掛載清理調(diào)用virtualizer._didMount()得到cleanup注冊(cè)到onScopeDispose(cleanup)組件銷毀時(shí)自動(dòng)解除 ResizeObserver、滾動(dòng)監(jiān)聽等副作用滾動(dòng)元素監(jiān)聽watch(() unref(options).getScrollElement(), ...)滾動(dòng)元素一旦就緒比如 ref 綁定完成就調(diào)用_willUpdate()選項(xiàng)響應(yīng)式同步上面的watch用setOptions把新選項(xiàng)含包裝后的onChange同步給 core并觸發(fā)_willUpdate()與triggerRef(state)返回state即RefVirtualizer模板中通過.value訪問實(shí)例方法。useVirtualizer額外通過computed注入三個(gè)默認(rèn)實(shí)現(xiàn)useVirtualizerBase( computed(() ({ observeElementRect: observeElementRect, observeElementOffset: observeElementOffset, scrollToFn: elementScroll, ...unref(options), })), )即元素模式下默認(rèn)使用observeElementRect/observeElementOffset基于 ResizeObserver 與 scroll 事件和elementScroll。useWindowVirtualizer則注入getScrollElement: () window、observeWindowRect、observeWindowOffset、windowScroll以及initialOffset: () window.scrollY見 源碼這些默認(rèn)實(shí)現(xiàn)與 virtualizer API 文檔 中描述的elementScroll/windowScroll/observeElementRect/observeWindowRect一一對(duì)應(yīng)。需要注意盡管文檔簽名寫作返回Virtualizer實(shí)際實(shí)現(xiàn)返回的是RefVirtualizerstateVue 模板與計(jì)算屬性中要用.value訪問——這正是 示例代碼 中rowVirtualizer.value.getVirtualItems()的寫法來源。版本演進(jìn)中的核心能力從 core 升級(jí)看功能增量雖然 Vue 適配器自身改動(dòng)極少但跟隨 core 的版本升級(jí)Vue 用戶也同步獲得了大量底層能力。以下是本倉庫 virtual-core CHANGELOG 中記錄的、隨 Vue 適配器各版本一起落地的關(guān)鍵能力core 3.16.0anchorTo: end 聊天/日志模式core 3.16.0對(duì)應(yīng) Vue 3.13.26 → 3.13.27引入了端錨定虛擬化專為聊天、日志、反向信息流設(shè)計(jì)新增anchorTo: end選項(xiàng)當(dāng)舊內(nèi)容被前插prepend時(shí)保持當(dāng)前可見項(xiàng)穩(wěn)定流式輸出中最后一項(xiàng)增長(zhǎng)時(shí)保持視口釘在底部默認(rèn)仍是start頂部/左側(cè)錨定保持原有行為新增followOnAppend只有視口原本就在末尾時(shí)新追加內(nèi)容才自動(dòng)滾入視野往上翻看歷史的用戶不會(huì)被拉回底部新增輔助 APIscrollEndThreshold、scrollToEnd()、getDistanceFromEnd()、isAtEnd()。這些 API 的語義在 virtualizer 文檔 中有完整定義scrollEndThreshold默認(rèn)1像素閾值isAtEnd(threshold?)判斷視口是否在距末端閾值范圍內(nèi)scrollToEnd()對(duì)縱向列表滾動(dòng)到底部。配套文檔還強(qiáng)調(diào)前插穩(wěn)定性要求基于持久 id 的穩(wěn)定getItemKey因?yàn)樗饕I無法區(qū)分前插與追加。3.16.1 又修復(fù)了一個(gè)前插時(shí)的「一幀跳躍」問題anchorTo: end下前插內(nèi)容時(shí)會(huì)有一幀按舊估算位置計(jì)算可見范圍隨后_willUpdate修正產(chǎn)生可見跳動(dòng)修復(fù)后在渲染過程中于setOptions內(nèi)提前調(diào)整scrollOffset使calculateRange/getVirtualItems立即返回正確條目。core 3.17.0useCachedMeasurements 與測(cè)量緩存core 3.17.0對(duì)應(yīng) Vue 3.13.28新增useCachedMeasurements選項(xiàng)見 virtualizer 文檔啟用后默認(rèn)measureElement跳過 DOM 讀取直接返回緩存尺寸無緩存則回退到estimateSize典型場(chǎng)景列表被臨時(shí)隱藏如父元素display: none時(shí)ResizeObserver 會(huì)對(duì)所有項(xiàng)報(bào)告尺寸 0導(dǎo)致測(cè)量被重置啟用該選項(xiàng)后隱藏期間測(cè)量不被清零恢復(fù)顯示后也不會(huì)出現(xiàn)布局跳動(dòng)使用方式是在隱藏前把該選項(xiàng)置true、顯示后置falseResizeObserver 始終保持掛載關(guān)閉后真實(shí)測(cè)量自動(dòng)恢復(fù)注意它只影響默認(rèn)measureElement自定義測(cè)量時(shí)需自行處理。3.17.0 還順帶優(yōu)化了默認(rèn)measureElement已有緩存時(shí)跳過同步 DOM 讀offsetWidth/offsetHeight減少重渲染時(shí)的 layout reflow。滾動(dòng)與測(cè)量正確性修復(fù)3.17.x 系列從 3.17.1 到 3.17.11 的密集 patch 主要打磨滾動(dòng)補(bǔ)償與測(cè)量時(shí)序這些修復(fù)全部隨 Vue 適配器 3.13.29 自動(dòng)獲得向上滾動(dòng)不跳動(dòng)3.17.1默認(rèn)滾動(dòng)補(bǔ)償謂詞在向上滾動(dòng)時(shí)也補(bǔ)償「估算→實(shí)測(cè)」首測(cè)差值但跳過重測(cè)補(bǔ)償避免級(jí)聯(lián)抖動(dòng)滾動(dòng)方向不誤鎖3.17.3、3.17.5虛擬器自身補(bǔ)償寫入觸發(fā)的滾動(dòng)事件不再被當(dāng)作backward方向鎖定避免多幀回流期間視口漂移減少 GC 壓力3.17.3默認(rèn)單車道路徑按滾動(dòng)幀零分配去掉每次滾動(dòng)事件上的選項(xiàng)對(duì)象與閉包分配gap 選項(xiàng)變化失效測(cè)量3.17.4gap 變更會(huì)失效測(cè)量緩存多車道m(xù)asonry布局改用增量車道 argmin替代反向掃描滾動(dòng)事件去重與端錨定同步3.17.2跳過相同 offset 的冗余滾動(dòng)事件applyScrollAdjustment中同步scrollOffset避免端錨定流式增長(zhǎng)時(shí)被瀏覽器 clamp 丟失iOS 處理3.17.5、3.17.6、3.17.7清理時(shí)重置 iOS 手勢(shì)/延遲狀態(tài)視口整體跨越折疊線的條目增長(zhǎng)不再默認(rèn)補(bǔ)償避免聊天流式消息被逐 token 拖拽iOS 延遲補(bǔ)償不再重放過期增量平滑滾動(dòng)存活3.17.11前插內(nèi)容時(shí)保持行進(jìn)中的平滑scrollToIndex存活anchorTo: end下不再被同步寫scrollTop打斷debounced 滾動(dòng)結(jié)束回退讀取當(dāng)前 offset避免被過期狀態(tài)覆蓋。這些條目同樣值得開發(fā)者關(guān)注如果你的 Vue 列表在聊天、日志、流式輸出、iOS 觸屏滾動(dòng)等場(chǎng)景遇到跳動(dòng)、漂移或釘?shù)资栴}對(duì)應(yīng)的修復(fù)版本就是排查與升級(jí)依據(jù)。Vue 中的實(shí)際用法從倉庫示例看標(biāo)準(zhǔn)接線固定/動(dòng)態(tài)尺寸的經(jīng)典寫法examples/vue/fixed 展示了基于固定尺寸的「行、列、網(wǎng)格」三種形態(tài)examples/vue/variable 展示了動(dòng)態(tài)尺寸寫法。核心接線方式以動(dòng)態(tài)為例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要點(diǎn)拆解getScrollElement: () parentRef.value返回滾動(dòng)容器配合適配器內(nèi)部的watch實(shí)現(xiàn)滾動(dòng)元素的響應(yīng)式綁定外層容器高度設(shè)為totalSize撐起整個(gè)滾動(dòng)區(qū)域每個(gè)虛擬項(xiàng)position: absolute; top: 0加transform: translateY(start px)絕對(duì)定位到對(duì)應(yīng)位置需要?jiǎng)討B(tài)測(cè)量時(shí)variable 場(chǎng)景給元素加:refrowVirtualizer.value.measureElement與data-index虛擬器會(huì)用 ResizeObserver 實(shí)測(cè)尺寸并逐步逼近真實(shí)高度。無限滾動(dòng)與 Vue Query 組合examples/vue/infinite-scroll/src/App.vue 展示了無限滾動(dòng)的完整模式useInfiniteQuery分頁拉數(shù)據(jù) →allRows合并所有頁 →useVirtualizer接收computed選項(xiàng)count: hasNextPage ? allRows.length 1 : allRows.length→ 用一個(gè)額外的 loader 行占位。關(guān)鍵觸發(fā)邏輯用watchEffect實(shí)現(xiàn)當(dāng)可見項(xiàng)中最后一項(xiàng)接近數(shù)據(jù)末尾且hasNextPage為真時(shí)調(diào)用fetchNextPage()watchEffect(() { const [lastItem] [...virtualRows.value].reverse() if (!lastItem) return if ( lastItem.index allRows.value.length - 1 hasNextPage.value !isFetchingNextPage.value ) { fetchNextPage() } })這個(gè)示例還體現(xiàn)了兩個(gè)適配器特性選項(xiàng)用computed傳入useVirtualizer(rowVirtualizerOptions)當(dāng)分頁數(shù)據(jù)增長(zhǎng)時(shí)count變化適配器會(huì)通過watchsetOptions把新選項(xiàng)同步給 core——這正是 3.13.13 修復(fù)所保證的「count 變化后高度自動(dòng)更新」在真實(shí)場(chǎng)景中的應(yīng)用。版本對(duì)照與升級(jí)建議結(jié)合本倉庫兩條 CHANGELOG 可以給出以下實(shí)用的版本追蹤方法看 Vue 包版本號(hào)tanstack/vue-virtual的 3.13.x 序列幾乎全部對(duì)應(yīng)tanstack/virtual-core的 3.13.x ~ 3.17.x只有 3.13.13 是適配器自身修復(fù)追 core 的 minor想要新能力聊天錨定、緩存測(cè)量、多車道優(yōu)化看 core 的 3.14.0、3.15.0、3.16.0、3.17.0 各自引入什么再映射到對(duì)應(yīng)的 Vue 版本3.13.24、3.13.25、3.13.27、3.13.28關(guān)注滾動(dòng)正確性修復(fù)3.17.x 系列密集修復(fù)了 iOS、平滑滾動(dòng)、錨定補(bǔ)償?shù)冗吔鐔栴}如果你的場(chǎng)景命中這些邊界優(yōu)先升級(jí)到較新的 3.13.39包結(jié)構(gòu)與構(gòu)建信息tanstack/vue-virtual以 ESM/CJS 雙格式發(fā)布見 package.json 的exports字段peerDependencies支持 Vue^2.7.0 || ^3.0.0sideEffects: false便于 tree-shaking。升級(jí)時(shí)建議參照倉庫的 workspace 結(jié)構(gòu)Vue 適配器與 core 在同一倉庫內(nèi)協(xié)同發(fā)版pnpm-lock.yaml與pnpm-workspace.yaml鎖定了版本關(guān)系本地開發(fā)可通過packages/vue-virtual目錄結(jié)合examples/vue下的固定、動(dòng)態(tài)、無限滾動(dòng)、padding、scroll-padding、smooth-scroll、sticky、table 等示例進(jìn)行驗(yàn)證。文檔側(cè)Vue 框架指南 提供組合式 API 的類型簽名Virtualizer API 文檔 提供全部選項(xiàng)與實(shí)例方法詳解兩者配合 CHANGELOG 閱讀即可獲得完整的使用與演進(jìn)視圖。贊分享前端UI組件【免費(fèi)下載鏈接】virtual Headless UI for Virtualizing Large Element Lists in JS/TS, React, Solid, Vue and Svelte項(xiàng)目地址https://gitcode.com/gh_mirrors/vi/virtual點(diǎn)擊查看免費(fèi)下載相關(guān)推薦TanStack Solid Query Devtools 演進(jìn)與實(shí)戰(zhàn)版本變更、核心原理與配置詳解TanStack Solid Query Devtools 演進(jìn)與實(shí)戰(zhàn)版本變更、核心原理與配置詳解 tanstack/solid query devtool前端緩存狀態(tài)管理Focalboard 版本演進(jìn)全解析從 v0.6 到 v0.15 的核心功能與實(shí)現(xiàn)原理Focalboard 版本演進(jìn)全解析從 v0.6 到 v0.15 的核心功能與實(shí)現(xiàn)原理 Focalboard 是一個(gè)開源、可自托管的項(xiàng)目管理工具定位為 Tr后端前端企業(yè)應(yīng)用桌面應(yīng)用協(xié)同辦公從 CHANGELOG 讀懂 eggjs/coreEgg 框架核心的版本演進(jìn)與底層機(jī)制從 CHANGELOG 讀懂 eggjs/coreEgg 框架核心的版本演進(jìn)與底層機(jī)制 本文以 packages/core/CHANGELOG.md htt后端Web框架上一篇如何 10 分鐘跑通 FinRobot多智能體股票研究平臺(tái)完整上手指南下一篇如何快速構(gòu)建高性能Web應(yīng)用Lwan與Lua集成的終極指南創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考