必须用 scroll-view 实现局部滚动,右侧为唯一滚动源并设固定高度和 scroll-y="true",左侧禁用滚动仅负责点击高亮;锚点定位需缓存 offsettop,滚动高亮应容忍±10px误差。

必须用 scroll-view 实现局部滚动,禁用页面级 onPageScroll;右侧是唯一滚动源,左侧只负责点击和高亮,不能滚动;scroll-into-view 和 scroll-top 不能混用,动态内容必须选 scroll-top + boundingClientRect 缓存。
右侧 scroll-view 必须设固定高度且启用 scroll-y
不设 height 或只写 100vh,真机上根本不会触发 @scroll。常见错误是套在 flex 容器里靠 flex: 1 撑高——H5 有时能蒙混过关,小程序和 App 端直接失效。
- 显式写
style="height: calc(100vh - 88px)"(减去导航栏高度),或用固定 rpx 值如height: 600rpx -
scroll-y="true"缺一不可,仅写@scroll不会监听滚动过程 - 如果用了
uni-nav-bar或自定义吸顶栏,计算 offsetTop 时要手动减去其实际像素值(88rpx ≈ 124px)
左侧禁用滚动,右侧单向驱动防抖动
两边都监听 @scroll 并互相改 scroll-top,结果就是 iOS 上抽搐式来回跳。这不是性能问题,是逻辑冲突。
- 左侧
scroll-view直接去掉scroll-y,让它纯静态展示;高亮靠class切换,不滚动 - 右侧才是唯一滚动源,所有联动动作(点击跳转、滚动高亮)都以它为基准
- 点击左侧时,用
this.$nextTick(() => { this.rightScrollTop = targetTop })而不是直接赋值,避免 DOM 未就绪
锚点定位必须缓存 offsetTop,不能每次滚动都查
在 @scroll 回调里反复调 uni.createSelectorQuery(),iOS 下卡顿明显,Android 上还可能查到 top: 0 的假值。
- 在
onReady后延时setTimeout(() => { queryAll() }, 100),比$nextTick更稳,确保v-for渲染完成 - 每个锚点节点加
position: relative,否则transform父容器会让boundingClientRect计算失准 - 缓存结果如
this.anchorTops = [{ id: 'cate-1', top: 0 }, { id: 'cate-2', top: 420 }],滚动时只做数组遍历比对
滚动高亮判定别用硬匹配,要容忍视觉误差
用 scrollTop === offsetTop 这种硬匹配,滑到边界时反复切换;滚动右侧时,高亮判定应基于视口重叠面积而非简单位置比较。
- 改用
scrollTop + visibleHeight * 0.3找最近锚点,容忍 ±10px 偏差 - 判断当前高亮项时,别用
===比较scrollTop和累计高度,要允许小范围误差 - 不要用
document.getElementById,uni-app 中必须走uni.createSelectorQuery()
最易被忽略的点是:所有锚点 id 必须合法(纯字母/数字/下划线,不能以连字符开头、不能含空格或中文),且必须位于 scroll-view 子元素第一层;嵌套一层 view 就找不到,微信小程序尤其严格。










