不能用 draggable 或 movable-view 做频道排序,因 draggable 在小程序中被忽略、h5 依赖浏览器、app 端手势冲突;movable-view 不感知列表结构,需手动计算插入点,逻辑复杂且跨端风险高;必须手动接管 touch 事件链,长按触发并加 300ms 防误触,动态查 boundingclientrect,用中心点对比定位,数组整体赋值更新,禁用 scroll-view 滚动,统一用 clienty 坐标,每次 touchmove 重新查 dom 位置。

为什么不能用 draggable 或 movable-view 做频道排序
draggable 属性在微信/支付宝小程序里直接被忽略,H5 行为依赖浏览器实现,App 端常因手势冲突失效;movable-view 是单块自由移动组件,不感知列表结构——松手时只返回 x/y 坐标,你得自己遍历所有 item 的 boundingClientRect 算插入点,逻辑复杂度和手写 touch 无异,还多一层跨端兼容风险。真要稳定支持 iOS 微信、安卓 WebView、H5、App-nvue,必须手动接管 touchstart/touchmove/touchend 事件链。
长按触发 + 动态查位置是唯一可靠路径
轻点就拖会吃掉点击事件(比如跳转或编辑),必须加 300ms 防误触阈值:
- 在
touchstart中记录this.startTime = Date.now()、this.startY = e.touches[0].clientY、this.dragIndex = e.currentTarget.dataset.index - 用
setTimeout延迟 300ms 激活拖拽态:this.dragTimer = setTimeout(() => { this.isDragging = true }, 300) -
touchmove中只做一件事:调用uni.createSelectorQuery()查当前 hover 区域附近 3–5 个 item 的boundingClientRect(),别一次性查全量(长列表卡顿) - 判断目标位置用「中心点对比」:
touchY > rect.top + rect.height / 2,比单纯比top更符合用户直觉
数组更新必须整体赋值,别 splice 原地改
原地 splice 容易因索引偏移插错位置,尤其向上拖时;Vue 响应式对数组索引变更敏感,局部修改可能丢失更新。正确做法是生成新数组再整体赋值:
const newArray = Array.from(this.list); const [movedItem] = newArray.splice(this.dragIndex, 1); newArray.splice(this.hoverIndex, 0, movedItem); this.list = newArray;
同时务必给每个 item 加唯一 :key="item.id"(不能用 index),否则列表复用节点会导致视图错乱。
scroll-view 必须禁用滚动,否则 iOS 小程序吞 touchmove
在包裹列表的 scroll-view 上加 scroll-y="false",否则 iOS 微信会抢走 touchmove 事件,导致拖拽中断或坐标漂移。另外:
- 所有坐标统一用
event.touches[0].clientY,别用pageY(小程序里恒为 0) - 调试时打印
event.touches[0].clientX和event.touches[0].screenX对比,能快速定位坐标系混乱 - 拖拽过程中不要调用
this.$forceUpdate(),靠key和数组整体赋值驱动视图更新即可
最易被忽略的是:每次 touchmove 都要重新查 DOM 位置,不能在 touchstart 缓存一次就完事——滚动或数据变化后位置已偏移,硬比坐标必然错位。










