唯一可控路径是使用 picker-view,因其所有 UI 元素均可自定义,而原生 picker 样式、动画、蒙层等均不可控且跨平台渲染不一致。

picker-view 替代原生 picker 是唯一可控路径
原生 picker 的蒙层、指示器颜色、字体大小、滚动动画全部不可定制,iOS 和 Android 渲染逻辑还不一致,改样式基本等于放弃。真正能自由控制外观和交互的,只有 picker-view —— 它本质是带滚动逻辑的容器,所有 UI 全由你写。
注意:不要试图给 picker 加 class 或覆盖 CSS,indicator-style 仅支持有限属性(如 color、height),且 H5 平台常失效;mask-style 在 iOS 微信里根本不起作用。
-
picker-view必须配合picker-view-column使用,内部用view渲染每一项,样式完全自主 - 所有文字、高亮、边框、蒙层都得手写,比如用
.item.active控制选中态 - 必须设
immediate-change="true",否则安卓真机滑动时不触发@change,只靠 value 变更驱动 - 指示器上下边框需额外用两个
view模拟,不能依赖原生 indicator
三列联动时 value 和 range 必须手动对齐
用 picker-view 做省市区三级联动,最常踩的坑是 value 越界或列间不同步——不是显示空白,就是滚动卡死。这不是 bug,是设计使然:它不校验 value[i] < range[i].length,越界就静默失败。
- 每次更新某列
range(比如选了“广东省”,第二列cities变成 21 个市),必须立刻把该列之后所有value设为0:this.cityIndex = 0; this.areaIndex = 0; -
setData必须传完整三元数组:{ provinceIndex, cityIndex, areaIndex },不能只传一个字段 - 每列
range数组长度必须 ≥ 对应value,否则 iOS 渲染异常、H5 不滚动 - 安卓真机单列超 200 项极易卡顿,建议截断并加 “更多…” 占位项,点击后跳转二级页
防抖 + $nextTick 是滚动流畅的关键
iOS 微信在快速滑动时,@change 会高频触发(一秒十几次),如果每次都在回调里直接 setData,视图更新会阻塞、回弹错乱、甚至丢帧。H5 平台还要求必须等 DOM 更新完再重置滚动位置,否则视觉错位。
- 加 100ms 防抖:用
clearTimeout+setTimeout包住后续逻辑 - 真机上必须用
this.$nextTick(() => this.setData(...)),H5 尤其严格 - 别在
@change里发起异步请求(如查下级数据),所有树形数据应提前加载完毕,运行时只做数组过滤 - 所有
range必须是纯字符串数组,含对象或函数会导致 iOS 渲染白屏
无限循环滚动只能靠数据模拟 + 位置重置
官方 picker 和 picker-view 都不支持真正的无限滚动。所谓“循环”,是靠生成超长重复数组 + 滚到边界时跳转中间位置实现的视觉欺骗。
- 原始数据
['A', 'B', 'C']扩展为 1000 项(重复 333 次 + 补余),总长totalLen - 监听
@change,当e.detail.value[0]≤ 3 或 ≥totalLen - 3时,计算新索引:midIndex = Math.floor(totalLen / 2) + (val % 3) - 然后
this.pickerValue = [midIndex],触发位置重置 - 性能敏感:重复次数别超过 200,否则内存暴涨;H5 下建议降为 50
真机调试时最容易忽略的是 iOS 微信对 picker-view-column 内 view 高度计算不准,导致滚动错位——务必统一用固定 line-height 和 height,别用 flex 或 padding 撑高。


















