左右联动菜单核心是scroll-view嵌套+scroll-into-view控制,左侧滚动高亮、右侧滚动自动定位;需节流监听、dom就绪校验、固定高度及避免样式干扰。

左右联动菜单的核心逻辑是 scroll-view 嵌套 + scroll-into-view 控制
uni-app 里没有原生的 sticky 或 sync-scroll,左右联动本质是「左侧菜单滚动时高亮对应项,右侧内容滚动时自动定位到对应菜单项」。关键不是监听 scroll 事件本身,而是利用 scroll-into-view 主动滚动左侧,用 scrollTop + getBoundingClientRect(通过 uni.createSelectorQuery)判断当前可视区域属于哪个分类区块。
常见错误:直接监听 scrolltoupper 或 scrolltolower,这两个只在滚动到顶/底才触发,无法实现精细联动;还有人试图用 scroll-y + bindscroll 频繁 setData,导致卡顿甚至白屏。
- 左侧菜单必须用
scroll-view包裹,且设scroll-y和固定高度(不能靠内容撑开) - 右侧内容区也必须是
scroll-view,禁用scroll-y的默认滚动条(加show-scrollbar="false"更干净) - 每个分类区块需有唯一
id(如category-0、category-1),用于scroll-into-view定位 - 不要在
onPageScroll里做 DOM 查询——它不触发于scroll-view,只作用于页面级滚动(即未使用scroll-view时)
监听右侧 scroll-view 滚动并匹配左侧高亮项
右侧 scroll-view 的 bindscroll 事件每帧都可能触发,但你不需要每次都查位置。建议节流(例如 throttle 50ms),并在每次触发时用 uni.createSelectorQuery() 获取所有分类区块的 top 值,对比当前 scrollTop 找出「当前最顶部且未完全滚出视口」的区块。
注意:boundingClientRect().top 返回的是相对于页面顶部的距离,而 scroll-view 内部坐标系需减去其容器 offsetTop —— 更稳妥的做法是统一用 scroll-view 自身的 scrollTop 和各区块的 offsetTop(通过 selectComponent 或 querySelector 获取)做差值判断。
- 给每个分类区块加 ref(如
:ref="'category'+index),在bindscroll回调中遍历 refs 获取offsetTop - 高亮逻辑:找出满足
item.offsetTop scrollTop的第一个项(+100 是容错偏移,避免临界点误判) - 找到后调用
this.$nextTick(() => { this.leftActiveIndex = index }),再让左侧scroll-view用scroll-into-view滚到对应id - 别忘了在 data 中定义
leftActiveIndex并绑定到左侧菜单class切换上
左侧点击跳转右侧时,右侧 scroll-view 必须能精准滚动到目标区块
左侧菜单点击后,不能只靠 scroll-into-view,因为右侧 scroll-view 可能还没渲染完(尤其用 v-for 动态生成时)。常见现象是第一次点击没反应,或滚动位置偏差。
根本原因是:点击瞬间,目标区块 DOM 还未挂载,scroll-into-view 查不到对应 id。解决方案是确保 DOM 就绪后再滚动。
- 点击后先
setData({ rightTargetId: 'category-' + index }),在watch中监听rightTargetId变化 - watch 回调里用
uni.createSelectorQuery().select('#' + this.rightTargetId).boundingClientRect()查询位置 - 查到后调用
this.$refs.rightScrollView.scrollIntoView(this.rightTargetId)(如果用 ref)或手动设置scrollTop - 若查询失败(返回 null),加一个
setTimeout重试(最多 2 次),避免死等 - 右侧
scroll-view的scroll-top属性要双向绑定(:scroll-top="rightScrollTop"),便于手动控制
真机调试时左右不同步?重点检查 scroll-view 的 height 和 overflow
H5 上表现正常,但微信小程序或 App 真机常出现「左侧不动」「右侧滚不动」「滚动卡顿」,90% 是样式问题:scroll-view 必须有明确高度,且父容器不能有 overflow: hidden 或 position: fixed 干扰。
另一个隐蔽坑:iOS 微信中,如果右侧 scroll-view 内部用了 flex: 1 或 height: 100%,会导致内部高度计算异常,boundingClientRect 返回的 top 偏差极大。
- 右侧
scroll-view的style必须写死高度,如height: calc(100vh - 100px)(减去头部和菜单高度) - 禁止在
scroll-view上设overflow: hidden,它自己会处理溢出 - 微信小程序中,若用
nvue渲染,scroll-view不支持scroll-into-view,必须改用scrollToAPI + 手动计算位置 - App 端开启
renderjs可提升滚动性能,但会增加包体积,非必要不启用
联动越顺滑,越说明你绕开了 setData 频繁触发和 DOM 查询时机错位这两个最常被忽略的点。











