scroll-view必须设固定高度并启用scroll-y,右侧为唯一滚动源,左侧禁用滚动;锚点需唯一id、position: relative,offsettop须缓存且查询时机要准。

scroll-view必须设固定高度且启用scroll-y
不设 height 或只写 100vh,scroll-view 在真机上根本不会触发 @scroll,更别提联动。常见错误是套在 flex 容器里靠 flex: 1 撑高——这在 H5 有时能蒙混过关,但在小程序和 App 端直接失效。
- 必须显式写
style="height: calc(100vh - 88px)"(减去导航栏高度),或用 rpx 固定值如height: 600rpx -
scroll-y="true"缺一不可,仅写bindscrolltoupper不会监听滚动过程 - 右侧列表每个分类区块必须带唯一合法
id,如cate-food,不能含空格、中文或连字符开头 - 如果用了
uni-nav-bar或自定义吸顶栏,计算 offsetTop 时要手动减去其实际像素值(88rpx ≈ 124px)
左侧禁用滚动,右侧单向驱动防抖动
两边都监听 @scroll 并互相改 scroll-top,结果就是 iOS 上抽搐式来回跳。这不是性能问题,是逻辑冲突。
- 左侧
scroll-view直接去掉scroll-y,让它纯静态展示;高亮靠class切换,不滚动 - 右侧才是唯一滚动源,所有联动动作(点击跳转、滚动高亮)都以它为基准
- 点击左侧时,用
this.$nextTick(() => { this.rightScrollTop = targetTop })而不是直接赋值,避免 DOM 未就绪 - 滚动右侧时,高亮判定别用
scrollTop === offsetTop这种硬匹配,改用scrollTop + visibleHeight * 0.3找最近锚点,容忍 ±10px 偏差
锚点定位必须缓存 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 }],滚动时只做数组遍历比对 - 不要用
document.getElementById,uni-app 中必须走uni.createSelectorQuery()
scroll-into-view 和 scroll-top 别混用
一个依赖 DOM 存在,一个依赖数值准确,混着用等于主动埋雷。异步加载图片、懒渲染商品项的场景下,scroll-into-view 会静默失败。
- 数据一次性加载、结构静态 → 用
scroll-into-view,代码少,:scroll-into-view="currentId"绑定即可 - 含图片、v-for 动态增删、小程序真机偏移 bug 高发 → 改用
scroll-top+boundingClientRect算出目标位置 - H5 端支持
scrollIntoView({ block: 'start' }),但小程序和 App 只认字符串 ID,且要求目标节点在scroll-view直接子层 - App 端 iOS 对
scroll-into-view有延迟,必须包一层$nextTick,否则查不到节点
真实项目里最易被忽略的是:锚点节点的 position 属性和查询时机。没加 position: relative,查出来的 top 就是错的;没等 onReady + setTimeout,缓存的就是一堆 0。这两点不解决,后面所有滚动逻辑都是空中楼阁。











