必须绕开四个硬坑:数据结构错、loading状态失控、iOS滚动卡死、H5列不联动;需严格树形结构、防重判序、弃multiSelector改picker-view、用slot自定义样式。

直接用 uni-data-picker 配合树形数据 + lazy-load 就能跑通,但真要自定义样式、跨端一致、不翻车,必须绕开四个硬坑:数据结构错、loading 状态失控、iOS 滚动卡死、H5 列不联动。
数据结构必须严格树形,children 不能为空数组
组件只认标准树节点:value、label、children(必须是数组)。后端返回的 children: null 或 undefined 会导致渲染中断或白屏。
- ✅ 正确写法:
{ value: 'zhejiang', label: '浙江', children: [{ value: 'hangzhou', label: '杭州' }] } - ❌ 错误写法:
{ id: 'zhejiang', name: '浙江', subs: null }—— 即使配了props映射,subs是null也解析失败 - ⚠️ 实操建议:在
lazy-load回调里统一做兜底:resolve(data?.children ?? []),别信后端字段一定存在
lazy-load 函数里必须手动防重、判序、控 loading
组件只调你的函数,不帮你管并发。用户连点两下“广东”,可能触发两次请求,后发先回,第二级列表就变成“广西”的市。
- 加唯一请求标识:
this.reqId = Date.now(),响应回来先比对res.id === this.reqId,不等就丢弃 -
loading必须显式绑定到组件的loadingprop,否则下拉时 UI 无反馈,用户会狂点 - 别在
lazy-load里直接this.$set或 push 响应式数组 —— 只管调resolve(data),组件内部会处理更新
H5 平台下 mode="multiSelector" 根本不联动
这是最隐蔽的坑:H5 上 mode="multiSelector" 只是三列独立滚动,不是真联动。选完省,城市列不会自动刷新,用户看到的还是上次的数据。
- 唯一可靠方案:放弃
picker,改用picker-view+ 手动逻辑 - 每次更新某列
range,必须立刻把该列之后所有列的value设为0,否则越界显示空白或跳回索引 0 -
bindchange触发后加setTimeout(() => {}, 100)防抖,避免 iOS 微信里滚动未停就刷新导致卡顿
自定义样式只能靠 slot + CSS 覆盖,别碰原生 picker 的 class
uni-data-picker 的弹层是原生组件,无法用普通 CSS 修改内部结构。想换颜色、改字体、加图标,只能走 slot 路线。
- 用
defaultslot 替换整个选择器触发区,自己画按钮 + 标签 + 箭头 - 弹层内容用
header和footerslot 控制顶部栏和确认按钮,但层级数据仍得靠组件内部渲染 - 真要完全自定义每一级样式(比如带 icon 的项、禁用态灰底),就得彻底放弃
uni-data-picker,手写picker-view+scroll-view组合
真正难的不是写代码,而是每次切换平台都要重新验证:iOS 滑动是否卡、H5 是否联动、小程序是否白屏、loading 是否同步。这些细节不压一遍真不知道哪行代码会在上线那天崩掉。


















