结论:swiper+vertical="true"+:current双向绑定是唯一稳定路径,需改用单video复用src或仅渲染current±1项,否则必卡顿白屏;@change需套$nexttick再操作video,ios微信current有延迟,首屏视频必须muted=true+autoplay=true并加playsinline属性,禁止一次性渲染全部video。

直接说结论:用 swiper + vertical="true" + :current 双向绑定是唯一稳定路径,但必须放弃“每个 item 都挂一个 video”的直觉,改用「单 video 复用 src」或「仅渲染 current±1 项」,否则 iOS 微信和安卓真机必卡顿、白屏、内存爆掉。
swiper 的 @change 不是滚动帧回调,而是分页对齐完成信号
很多人在 @change 里直接调 this.$refs.video.play(),结果首帧黑屏或播放延迟——因为 DOM 还没渲染完,video 标签可能还没挂载到页面上。
- 必须在
@change回调里包一层this.$nextTick(() => { ... }),再查this.$refs.video - iOS 微信中
current更新有微小延迟,别拿它算进度条或同步动画 - 如果视频列表动态追加(比如下拉加载),更新
videoList后要手动重置currentIndex,否则容易越界或卡死在空白页 - 安卓和 H5 下
@change响应及时;iOS 微信 WebView 中可能合并多次快速滑动,只触发一次
video 在 iOS/H5 上“有声无画”的真实原因和解法
不是代码写错了,是 Safari 和微信 WebView 的硬性策略:没有用户手势触发前,autoplay="true" + muted="false" 必然失败。表现就是音频解码成功、视频帧卡第一帧。
- 首屏视频必须设
:muted="true"+:autoplay="true",让画面先出来 - 监听
@loadedmetadata或@canplay,就绪后立刻调videoContext.mute(false)尝试开声(部分机型仍需用户点一下屏幕) - 所有
video必须加playsinline、webkit-playsinline、x5-playsinline,否则 iOS 微信强制全屏 - H5 端首次加载后,必须等用户触发一次
touchstart,后续play()才真正可用
为什么不能一次性渲染全部 video 标签
一次性把 100 条视频都塞进 swiper-item,等于告诉浏览器:“请同时解码 100 个视频首帧”。iOS Safari 直接卡死,小程序白屏,App 端内存飙升到 500MB+。
- 只保留
current - 1、current、current + 1共 3 个video激活状态,其余用v-if="false"彻底移除 DOM - 每个
video加preload="metadata",而非"auto",减少预加载负担 - 滑出视口 500ms 后,执行
pause()+src = ''+load(),释放解码资源 - 小程序中慎用
autoplay:设为false,等@loadedmetadata触发后再play(),避免并发加载阻塞主线程
swiper-item 高度和全屏控制的真机陷阱
:vertical="true" 必须配 height: 100vh,但某些安卓 WebView 会把地址栏高度算进 vh,导致底部留白;iOS Safari 又可能因 URL 栏收起/展开动态改变 vh。
- 推荐在
onLoad阶段通过uni.getSystemInfoSync().windowHeight动态计算容器高度 - 全屏逻辑不要依赖
video自身的requestFullScreen(),H5 端只能模拟,且部分安卓浏览器禁止自动全屏 -
抖音式体验关键点:全屏时不显示
controls,用自定义按钮控制播放/点赞,退出全屏后要恢复原scroll-view位置(需记录lastScrollTop) - 真机调试时,iOS 微信环境需额外加
enable-play-gesture="true"才允许手势触发全屏
最常被忽略的不是“怎么切”,而是“什么时候销毁、什么时候预加载、什么时候强制暂停”——这些边界点稍有偏差,用户划着划着就卡住或黑屏了。











