uni-app 的 <video> 组件跨端差异极大,必须分端适配:iOS/小程序强制 HTTPS、路径须用 /static/ 绝对路径;Android 推荐 H.264 Main Profile MP4;小程序仅支持 MP4 且需 <cover-view> 覆盖;H5 需手动同步 currentTime;HLS/FLV 仅 H5 可通过 hls.js 支持;全屏/横屏需原生 API 控制。

直接说结论:uni-app 的 <video> 组件不是“写完就能播”,它在 iOS、Android、小程序、H5 各端底层调用的原生播放器完全不同,格式、路径、事件、全屏逻辑全部不一致——必须分端处理,否则真机必出问题。
为什么真机上视频黑屏或报 “The element has no supported sources”
这不是你 src 写错了,是跨端加载机制差异导致的静默失败:
- iOS 和微信小程序强制要求 HTTPS,HTTP 地址在真机上直接拒绝加载,控制台无提示
- 资源路径不能用
./videos/xxx.mp4这类相对路径,必须放static/目录下,用绝对路径如/static/videos/demo.mp4 - App 端 Android 对编码极其敏感,推荐用 H.264 Main Profile + AAC 的 MP4,编码参数为
avc1.42E01E;iOS 则更认 Baseline Profile - H5 端支持
.mp4、.webm;小程序只认.mp4;App 端不支持 HLS(.m3u8)和 FLV,传进去只会显示“无法播放”
controls="false" 后还是有原生控件?这是平台在“偷偷加料”
各端对 UI 的劫持方式不同,单纯隐藏 controls 没用:
- iOS WebView 会强制注入
webkit-video-controls样式表,哪怕 DOM 不渲染,底层仍存在可点击区域(尤其playsinline=false时) - 微信小程序中
<video>是原生组件,z-index 永远最高,普通<view>覆盖无效;必须用<cover-view>,且父级需设pointer-events: auto(基础库 2.27.0+ 才支持) - H5 端看似自由,但 Chrome 120+ 对
input[type="range"]::-webkit-slider-thumb做了样式隔离,CSS 覆盖常失效;拖动后必须手动执行video.currentTime = value,仅靠v-model会跳回原位置
想播 .m3u8 或直播流?别硬塞给 <video>
uni-app 官方 <video> 在非 H5 端完全不支持 HLS/FLV/MSE,强行使用等于放弃 App 和小程序:
- H5 端可用
hls.js:监听@loadedmetadata后初始化,用hls.loadSource(url)接管;注意要兼容 Safari(需 fallback 到原生<video>) - 小程序端必须换方案:用官方
live-player(仅支持直播)或第三方插件如uni-m3u8-player - App 端需原生插件支持,例如
uni-plugin-html5-video或自定义plus.video.Player封装,seek精度较低,别依赖毫秒级定位
全屏和横屏在 App 端为啥不生效
direction="1" 这类属性只对小程序有效,App 端由原生层控制屏幕方向,<video> 组件无权干预:
- 全局横屏:在
manifest.json中配置"orientation": "landscape" - 动态锁定:在
onReady后调用plus.screen.lockOrientation("landscape-primary") - 全屏按钮点击后,必须监听
fullscreen-change事件,在回调里手动调用原生 API;安卓部分厂商(华为、小米)会拦截requestFullscreen,需检查nativePlugins权限配置
真正难的不是画一个播放器 UI,而是让同一套业务逻辑在五种底层播放器上保持行为一致——iOS 的 AVPlayer、Android 的 VideoView、小程序的 native video、H5 的 HTMLMediaElement、鸿蒙的 MediaKit,它们对 timeupdate 频率、seek 可达性、错误码定义、甚至暂停后是否清空缓冲区,全都不同。别指望一次适配全端,先保 H5 和小程序,再啃 App。



















