结论是:不用onPageScroll、不手动设scrollTop,核心是左侧点击触发右侧scroll-into-view滚动到对应id,右侧滚动时通过缓存offsetTop数组节流比对实现左侧高亮,其余均为配套优化。

直接说结论:不用 onPageScroll,不用手动设 scrollTop 硬算,核心就两条——左侧点哪个,右侧用 scroll-into-view 滚到对应 id;右侧滚到哪,左侧靠缓存的 offsetTop 数组 + 节流比对来高亮。其他都是围绕这两条打补丁。
为什么不能用 onPageScroll 做联动
onPageScroll 只响应整页滚动,而美团式侧边栏必然放在 scroll-view 里(否则左侧菜单没法固定、锚点对不齐)。你在 scroll-view 里滚动,onPageScroll 根本不触发。真机上 iOS 完全收不到,Android 即使收到,scrollTop 单位和 scroll-view 内部也不一致,换算极易出错。
- 检查你模板里是不是写了
<scroll-view scroll-y>—— 如果没写,说明你根本没用局部滚动,那才该用onPageScroll - 如果用了
scroll-view还硬套onPageScroll,iOS 下静默失效,Android 下位置偏移,这不是 bug,是机制错配 - 正确监听方式只有
@scroll(绑定在scroll-view上),取event.detail.scrollTop
scroll-into-view 失效的三个高频原因
这个 API 在不同端行为差异极大,失效往往不是写法错,而是端间兼容没兜住。
- H5 支持
scrollIntoView({ block: 'start' }),但 App 和小程序只认scroll-into-view属性绑定的字符串id,传对象直接忽略 - 微信小程序要求目标
id必须是scroll-view的直系子元素,嵌套一层<view><view id="xxx"></view></view>就找不到,得扁平写成<scroll-view><view id="xxx"></view></scroll-view> - App 端(尤其 iOS)对
scroll-into-view响应有延迟,必须加this.$nextTick(() => { ... }),否则节点还没挂载就去滚动,静默失败
右侧滚动时左侧高亮错位怎么办
错位本质是高度基准没对齐,或判断逻辑太敏感。别用 scrollTop === offsetTop 这种硬匹配,要容错、要节流、要减去顶部遮挡。
- 所有锚点节点(如
<view id="cate-1">)必须加position: relative,否则父级transform会让boundingClientRect计算失准 - 查询时机很重要:在
onReady后加setTimeout(() => { queryAllCateHeights() }, 100),比单纯$nextTick更稳妥 - 计算当前高亮项时,用
rightScrollTop + window.innerHeight * 0.3当作“可视区中心线”,而不是直接比scrollTop,能缓解临界点反复切换 - 如果页面有
uni-nav-bar或自定义 header,记得从boundingClientRect().top里减去其实际高度(比如 88rpx → px 值)
左右滚动抖动、抽搐怎么解决
两边互相监听、互相设 scroll-top,就会形成循环触发。快速拖拽时,scrollTop 被来回覆盖,视觉上就是抽搐。
- 根本解法是「单向驱动」:右侧
scroll-view是唯一滚动源,左侧禁用scroll-y,只响应点击,靠scroll-top被动跟随 - 左侧
scroll-view去掉scroll-y,保留ref,需要滚动时用this.$refs.leftScroll.scrollIntoView(id),而不是设scroll-top - 右侧
@scroll回调里,只更新currentActiveId,不反向操作左侧的scroll-top - 如果必须让左侧也能滚动(比如长分类列表),那就只监听
@scrolltoupper/@scrolltolower做粗粒度跳转,避免高频冲突
最易被忽略的点是:右侧每个分类区块的 id 必须静态、唯一、全小写、无空格或特殊字符——category-food 可以,categoryFood 或 category food 都会失败,且不报错,静默卡住。



















