fix(tabs): 修复 showMoreTabs 模式下 overflow 计算滞后导致下拉菜单未及时更新(#4279) - #4272
fix(tabs): 修复 showMoreTabs 模式下 overflow 计算滞后导致下拉菜单未及时更新(#4279)#4272Georgyhongbo wants to merge 2 commits into
Conversation
…ansition The nav element has CSS transition: transform 0.3s, which causes getBoundingClientRect() to return pre-transition positions when calcMorePanes runs in requestAnimationFrame (before the transition has advanced). Fix: use tab-relative-to-nav position delta (transition-invariant since both share the same transform coordinate space) plus navOffset to compute the final expected positions, bypassing the transition intermediate state entirely. Also replace nextTick with requestAnimationFrame in deferred calcMorePanes calls to ensure all cascading DOM updates from scrollToActiveTab -> updated() chains have settled.
WalkthroughTabs overflow detection now uses tab and navigation bounding rectangles. Recalculation is exposed through the renderless API and deferred with ChangesTabs overflow recalculation
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@shenjunjian 能麻烦审核一下吗,谢谢 |
@shenjunjian Could you please review it? Thank you. |
PR
PR Checklist
PR Type
What is the current behavior?
Tabs 组件在
showMoreTabs模式下,"更多"下拉菜单的溢出计算(calcMorePanes)存在滞后问题,表现为以下场景:初始加载时:不可见的 tab 中,最靠左的一个没有出现在下拉菜单中(例如 tabs 1-5 可见,6-8 不可见,但下拉菜单只有 7、8,缺少 6)
从下拉菜单点击 tab 后:
scrollToActiveTab将 nav 滚动到目标 tab,使其变为可见,但下拉菜单中的内容不会更新——已变为可见的 tab 仍然显示在下拉菜单中点击可见 tab 后:上一步下拉菜单中滞留的 tab 此时才消失,表现为"慢一拍"——上一个动作的结果在下一个动作时才结算
连续操作:从下拉菜单选 tab8 → 点回 tab4 → 点
<箭头 → 下拉菜单打开失败或内容错误根因有两点:
a) CSS transition 干扰坐标读取:
nav元素上有transition: transform 0.3s。当scrollToActiveTab改变navOffset触发滚动时,tab 位置通过 CSS 动画在 0.3s 内逐渐过渡到新位置。但calcMorePanes在requestAnimationFrame回调中运行——此时 transition 尚未推进(动画尚未开始或刚处于第 0 帧),getBoundingClientRect()返回的是过渡前的位置,导致溢出判断基于旧坐标。b) 级联 DOM 更新未充分等待:
scrollToActiveTab修改navOffset→ TabNav 重渲染 →updated()回调可能再次调整navOffset(clamping)→ 再次重渲染。这是一个级联的微任务链,nextTick(Promise.then)只能等一轮 flush,无法保证所有级联更新都已落定。What is the new behavior?
所有场景下下拉菜单内容与可见 tab 保持同步:初始加载、从下拉菜单点击 tab、点击箭头滚动后,下拉菜单始终只显示真正不可见的 tab
消除"慢一拍"滞后:点击 tab 后立即正确计算溢出,不再需要下一个动作才结算
修复方案:
a) Transition 不变公式(
packages/renderless/src/tabs/index.ts):用 tab 相对于 nav 的位置差(tabRect.left - navRect.left)替代直接使用tabRect.left + tabRect.width / 2。因为 tab 和 nav 共享同一个transform坐标系,CSS transition 对两者的偏移量始终相同,差值恒定不变。再结合目标navOffset值推算 transition 完成后的最终视口位置。该公式在有无 transition 的情况下均数学等价于旧公式,但不受 transition 中间状态干扰。b) requestAnimationFrame 替代 nextTick(
packages/renderless/src/tabs/vue.ts、packages/vue/src/tabs/src/pc.vue):在 4 处异步刷新点(onMounted、onUpdated、currentNamewatcher、模板 render)用requestAnimationFrame延迟calcMorePanes调用。rAF 在所有微任务(包括级联的 Vue 更新)完成后、浏览器绘制前触发,确保读取到的 DOM 状态是最终落定的。Does this PR introduce a breaking change?
Other information
改动文件:
packages/renderless/src/tabs/index.tscalcMorePanes:用 transition 不变的几何公式替代直接坐标比较packages/renderless/src/tabs/vue.tspackages/vue/src/tabs/src/pc.vueshowPanesCount的语义不变(第一个不可见 tab 的索引,等于 tabs.length 时表示无溢出)。新增的 rAF 调用是对即时calcMorePanes()的补充,即时调用仍然保留以确保首次渲染和scrollPrev/scrollNext等场景的即时响应。Summary by CodeRabbit