position: sticky 在微信、支付宝等平台未被实现,真机失效;唯一全端稳定方案是 scroll-view + bindscroll,需显式设 height、禁用页面滚动、缓存 offsettop、用占位节点防跳动,list-view 场景须用官方 sticky-header 组件。

为什么 position: sticky 在分组列表里完全不 work
不是你 CSS 写错了,是微信、支付宝、百度、App 等平台压根没实现 position: sticky。哪怕 H5 端看着动了,真机一跑就回归文档流——因为运行时直接忽略该声明。常见现象:iOS 微信里标题“跳回”顶部、安卓机上吸顶区域随列表一起滚走、开发者工具正常但预览失效。
scroll-view + bindscroll 是唯一全端稳定方案
核心不是“怎么写样式”,而是“什么时候切 fixed”和“切得准不准”。必须满足以下条件:
-
scroll-view必须显式设置height(如height: calc(100vh - 100rpx)),禁用页面级滚动("pageStyle": "overflow: hidden") -
offsetTop必须在onReady或nextTick后获取,onLoad里取大概率返回0 - 每个分组头部(如
category-title)需设唯一id(如id="cate-1"),不能只靠 class 查询 - 用
uni.createSelectorQuery()获取实际top值判断是否到达视口顶部,别直接用scrollTop做固定阈值 - 切换
is-sticky状态时,必须配占位节点:<view v-if="isSticky" class="placeholder" :style="{ height: headerHeight + 'px' }"></view>,否则内容上跳
list-view 场景下只能用 sticky-header 组件
如果你用的是 list-view(非 scroll-view),bindscroll 不触发、offsetTop 取不到——因为底层基于原生 RecyclerView / UITableView,DOM 不完整。此时必须用官方 sticky-header 组件:
- 它必须是
list-view的**一级子节点**,不能嵌套在view或其他容器里 - 一个
list-view只能有一个sticky-header;如需多级吸顶(如通讯录字母分段),得配合sticky-section -
sticky-header内部不能含input、picker等原生组件,iOS 微信中 fixed 容器会被键盘顶起且无法修正
性能敏感场景必须节流,但不能乱节流
频繁触发 bindscroll 会导致低端安卓卡顿。简单 setTimeout 节流会滞后,必须用 requestAnimationFrame:
- 只在下一帧执行一次定位计算,避免重复
setData - 仅当
scrollTop跨越吸顶阈值(如从isSticky === false变为true)才更新状态 - 禁止在
bindscroll回调里调用uni.createSelectorQuery()或getComputedStyle()——这些是同步阻塞操作 - 所有高度、位置信息应在
onReady中一次性缓存,滚动中只做比对
吸顶的真正难点不在“怎么悬停”,而在“不同平台滚动模型差异导致的定位漂移”——iOS 微信 dpr、字体缩放、Android WebView 渲染延迟都会让 top 值偏移几个像素,必须靠元素实际位置而非固定数值判断。











