抖音式垂直翻页核心是精准分页,需用swiper vertical="true" + :current配合@change事件,禁用滚动监听;视频须muted+autoplay+playsinline,动态管理可视区dom,ios需特别处理手势与全屏逻辑。

swiper vertical="true" + :current 是分页切换核心,不是滚动监听
抖音式垂直翻页本质是“精准分页”,不是滚动过程控制。uni-app 的 swiper 组件必须用 :vertical="true" 和 :current="currentIndex" 配合 @change 事件,才能稳定对齐每一页。别指望 @touchmove 或 @transition 实时捕获滑动位置——它们在 iOS 上延迟大、不准确,且 @change 才是页面完全停稳后的唯一可靠信号。
常见错误是把 currentIndex 当作“正在滑动的目标页”提前触发播放,结果 DOM 还没渲染完,this.$refs.video.play() 直接报错或静默失败。正确做法是:在 onSwiperChange 回调里,先 this.$nextTick(() => { ... }) 确保 video 元素已挂载,再操作播放状态。
- iOS 微信 WebView 中,
current更新可能滞后 1–2 帧,别拿它算进度条或做同步动画 - 视频列表动态追加后,必须手动重置
currentIndex(如this.currentIndex = Math.min(this.currentIndex, this.videoList.length - 1)),否则容易越界卡白屏 - H5 端安卓响应快,iOS 则倾向合并快速滑动事件,
@change可能只触发一次
video 必须 muted + autoplay + playsinline 才能首帧出画
iOS Safari 和微信 WebView 对带声自动播放有硬限制:没用户手势前,autoplay="true" + muted="false" 必然失败,表现就是“有声无画”或纯黑屏。这不是 bug,是策略。绕不开,只能顺应。
首屏视频必须设 :muted="true" + :autoplay="true",让画面先出来;等 @canplay 或 @loadedmetadata 触发后再调 videoContext.mute(false) 尝试开声(部分机型仍需用户点一下屏幕)。所有 video 标签还必须带 playsinline、webkit-playsinline、x5-playsinline,否则 iOS 微信强制全屏,破坏沉浸感。
- H5 首次加载后,必须等用户触发一次
touchstart,后续play()才真正可用 - 小程序中慎用
autoplay,建议设为false,等@loadedmetadata后再play(),避免并发加载阻塞主线程 - 抖音小程序要求
enable-danmu="false",否则首帧黑屏
只渲染当前页 ±2 个 item,否则 iOS 直接卡死
一次性把 50+ 条视频塞进 swiper-item,等于让浏览器同时解码 50 个视频首帧。iOS Safari 内存飙升、掉帧、白屏是常态,小程序直接崩溃。这不是优化问题,是必须规避的硬伤。
真实项目必须做“可视区+缓冲区”动态管理:只渲染 videoList.slice(currentIndex - 2, currentIndex + 3) 共 5 个 swiper-item,其余全部用 v-if="false" 彻底移除 DOM。每个 video 加 preload="none",首次进入可视区再调 .load();滑出视口 500ms 后调 .pause() 并释放资源。
- 别用
v-show,它只是display: none,video 依然在后台加载元数据 - swiper-item 必须设
overflow: hidden,防止 poster 或 loading 动画溢出干扰视觉 - 高度必须用
100vh,但注意安卓 WebView 可能把地址栏算进 vh,iOS Safari 又会因 URL 栏收起/展开动态变高——建议用height: calc(100vh - var(--safe-area-inset-top))+env(safe-area-inset-bottom)
scroll-view 不适合包 video,事件穿透和 z-index 都不可控
直接用 scroll-view 包 video 是典型误区。iOS/安卓 WebView 中,video 是原生控件,事件穿透被拦截,z-index 失效,你看到的“滑动”其实是页面滚动,不是视频本身在动。
真机调试时,iOS 微信环境必须加 enable-play-gesture="true",否则点按无效;而 scroll-y="true" + bounce="false" 是基础配置。但更稳妥路径是:放弃 scroll-view,改用普通 view + position: sticky 或 transform: translateY 模拟滚动,靠 uni.createSelectorQuery() 获取当前视频是否居中(±15% 视口范围),再手动控制播放。
- 每个 video 加
:ref="'video' + index,onReady后缓存实例,避免重复查询 - H5 端可用
IntersectionObserver替代手动计算,但小程序不支持,需条件编译 - 全屏逻辑各端差异大:微信小程序调
requestFullScreen()进系统页;H5 只能模拟,且需用户手势触发
真机上最容易被忽略的是三件事:没做 video 实例销毁、没限制预加载数量、没关非活跃项的 poster 动画。这些细节不处理,哪怕逻辑写对了,iOS 上照样卡成 PPT。











