尧图精选

Vue3视频播放器集成指南:@videojs-player/vue从入门到HLS实战

🕒 发布时间:2026/10/2 18:19:16 📁 来源:尧图网络
开头直接切入不需要标题直接以对话式进入。第一次在 Vue3 后台管理系统里集成视频播放器我图省事直接找了一个看起来最顺眼的组件库自带播放器结果半个小时就放弃了。不是样式太丑就是 API 设计得太有个性要么干脆和 Vue3 的响应式系统八字不合。后来换了videojs-player/vue这套基于 Video.js 的 Vue3 封装才算真正把在项目里播视频这件事彻底打通。这篇文章我就把这套组件从安装、基础用法、事件监听、实例访问到 HLS 直播流、多清晰度切换、皮肤定制的完整链路都捋一遍把我实际开发中踩过的坑也一并交代清楚。适合正在做 Vue3 后台、数据可视化大屏、商城或者任何需要内嵌视频播放器的前端同学参考。1. 为什么我在 Vue3 项目里最终选了 videojs-player/vue先说结论Vue3 生态里视频播放器的封装方案不少但 videojs-player/vue 是目前少有的、同时兼顾了保留原生 Video.js 能力和Vue3 响应式开发体验的封装。在最终敲定这套方案之前我实际对比过几条路方案优点缺点直接用原生video标签零依赖、最简单控件样式的浏览器差异巨大功能全靠自己造轮子element-plus / antd-vue 等 UI 库自带播放器风格统一、上手快实际就是个阉割版video功能太少HLS、清晰度切换基本没戏vue3-video-play 等轻量封装API 简洁扩展性弱想深入定制 Video.js 插件时无从下手videojs-player/vue完整保留 Video.js 全部能力提供 Vue3 组件式 API需要额外理解 Video.js 本身的概念我的判断标准其实就三条第一底层必须是 Video.js因为它是目前最成熟的开源 HTML5 视频播放器框架插件生态丰富第二封装层要贴合 Vue3 的响应式写法不能每个配置项都要手动 ref 去同步第三遇到复杂场景直播流、多清晰度、自定义字幕时得能拿到原生的 player 实例。同时满足这三条的就是 videojs-player/vue。这个组件库的本质是一层翻译官把 Vue3 的 props、事件、响应式数据翻译成 Video.js 底层的 options、事件回调和 player 实例方法。它的核心依赖video.js本身并不关心你用的什么前端框架而这一层封装让你的代码写起来像在写 Vue 组件而不是在操作 DOM。这里插一句版本问题。截止我写这篇文章时videojs-player/vue 的 2.x 版本对应 Vue31.x 版本是给 Vue2 用的两者 API 有差异。如果你的项目是 Vue3安装的时候一定要确认装的是latest不要复制老教程里的npm install videojs-player/vue就直接跑因为 npm 默认安装的确实是最新版但网上大量文章还停留在 Vue2 时代的写法。2. 安装与版本选型依赖关系和兼容性检查安装命令很简单但有两个坑要提前说。npm install videojs-player/vue video.js注意这里video.js也需要显式安装。videojs-player/vue 把 video.js 放在了peerDependencies对等依赖里意思是你需要自己在项目里安装它。某些老版本的 npm 不会自动安装 peerDependencies你不装的话跑起来直接报Cannot find module video.js。安装完成之后我建议你打开package.json确认一下版本dependencies: { videojs-player/vue: ^2.0.0, video.js: ^8.x.x }video.js 在 2023 年底左右发布了 8.x 版本和 7.x 相比在 CSS 结构和一些内部 API 上有调整。videojs-player/vue 2.x 对这两者都兼容但如果后续你要自己写 Video.js 插件建议以你实际安装的版本为准去查对应版本文档因为 7 和 8 在videojs.registerPlugin等方法上行为一致但在部分 UI 覆写上不一样。安装完成后在组件里引入的方式有两种。方式一在 SFC 里直接引入推荐script setup import { VideoPlayer } from videojs-player/vue import video.js/dist/video-js.css /script template VideoPlayer srchttps://vjs.zencdn.net/v/oceans.mp4 / /template方式二全局注册在main.js中import { createApp } from vue import App from ./App.vue import { VideoPlayer } from videojs-player/vue import video.js/dist/video-js.css const app createApp(App) app.component(VideoPlayer, VideoPlayer) app.mount(#app)两种方式我都用过如果你只是在某个页面里需要播放器用第一种局部引入就够了还能让打包体积按需走。如果整个后台系统很多地方都要用第二种全局注册写起来更省事。还有一点记得引 CSS。很多人安装完播放器发现一坨原生 HTML 控件堆在页面上样式全没生效就是漏了import video.js/dist/video-js.css这一步。3. 基础用法几十行代码跑通第一个播放器理论铺垫结束直接看代码。跑通一个最基础的可播放视频模板里就一行template VideoPlayer :srcvideoSrc autoplay controls stylewidth: 100% / /template script setup import { ref } from vue import { VideoPlayer } from videojs-player/vue import video.js/dist/video-js.css const videoSrc ref(https://vjs.zencdn.net/v/oceans.mp4) /scriptautoplay是自动播放controls是显示控制条。src是视频地址你可以直接传一个 mp4 链接也可以传一个包含多清晰度的对象。我们后面专门讲。先别急着跑这里有个容易被忽略的点VideoPlayer 组件渲染出的 DOM 结构里默认包含一个video标签和 Video.js 生成的整套控件层。它的根容器是一个div这个 div 默认没有高度。如果你不给它设置宽度和高度或者它的父容器没有明确高度播放器可能只显示一条进度条或者干脆看不到。我一般习惯给它一个固定的容器div stylewidth: 800px; height: 450px VideoPlayer src... stylewidth: 100%; height: 100% / /div或者用 class 控制VideoPlayer classvideo-wrap src... /.video-wrap { width: 100%; aspect-ratio: 16 / 9; }这里用aspect-ratio是 CSS 里最省事的写法播放器会一直保持 16:9 的比例不需要在 JS 里算高度。Safari 的某些老版本对 aspect-ratio 支持不太行但后台系统基本都是 Chromium 内核实测下来问题不大。跑通了基础播放之后你会发现 Video.js 的默认皮肤是深色底、蓝色高亮的和很多后台系统的设计风格不太搭。这个我们留到视觉定制那一节专门处理。4. 核心配置项拆解每个属性背后的行为差异videojs-player/vue 的options属性对应 Video.js 的配置对象但组件同时放开了很多顶层 props让你可以直接通过属性方式传递。先看一个稍微完整一点的例子template VideoPlayer :srcvideoSrc :optionsplayerOptions autoplay controls :loopfalse :mutedfalse :playsinlinetrue preloadauto readyonPlayerReady playonPlay pauseonPause endedonEnded timeupdateonTimeUpdate erroronError / /template4.1 最常用的顶层 props 详解属性名类型默认值作用srcstring / object无视频地址支持字符串和 Video.js source 对象posterstring无封面图地址controlsbooleantrue是否显示控制条autoplayboolean / stringfalse是否自动播放。传入muted表示静音自动播放mutedbooleanfalse是否静音loopbooleanfalse是否循环播放preloadstringmetadata预加载策略none/metadata/autoplaysinlinebooleanfalseiOS Safari 内联播放crossoriginstring无CORS 属性posterstring无封面图optionsobject无传给 Video.js 的完整配置有两个属性你要特别留意因为它们的默认值和直觉相反。controls默认是true但如果你通过options传入配置时没写controlsVideo.js 的行为是显示控制条。而如果你在 options 里写了controls: false又同时用 props 传了controlsprops 的优先级更高最终还是会显示。autoplay建议不要直接传true。现代浏览器的自动播放策略非常严格带声音的自动播放几乎都会被浏览器拦截。如果你确实需要进页面自动播放最好传muted即静音自动播放。但需要注意当传递muted时即使播放器界面上显示的是静音状态用户点一下按钮就能恢复声音体验并不差。4.2 options 里的几个进阶配置除了顶层 props更多底层能力是通过options传入的。下面这个配置覆盖了我日常最常用的场景const playerOptions { autoplay: false, controls: true, preload: auto, language: zh-CN, playbackRates: [0.5, 1, 1.5, 2], // 倍速播放 controlBar: { volumePanel: { inline: false }, pictureInPictureToggle: true // 画中画按钮 }, notSupportedMessage: 当前浏览器不支持播放该视频, sources: [ { src: https://vjs.zencdn.net/v/oceans.mp4, type: video/mp4 } ] }language: zh-CN这句话很多人会忽略。Video.js 默认是英文界面不设置的话你的播放器显示的是PlayMuteSettings这些英文后台管理系统里给客户演示时非常掉价。设置成zh-CN后按钮文本会变成中文。不过要注意一点中文化依赖 Video.js 的语言包。Video.js 8.x 内置了zh-CN语言包你不需要额外下载直接设置language: zh-CN即可。如果你用的是 7.x可能需要手动引入video.js/dist/lang/zh-CN.json并注册这点版本之间差异比较大。playbackRates是倍速播放列表。默认 Video.js 是没有倍速按钮的加上这个配置后控制条会出现一个速度选项用户可以选择 0.5、1、1.5、2 倍速。4.3 src 和 options.sources 的区别这是最让我一开始踩坑的地方顶层srcprop 和 options 里的sources数组是什么关系其实很简单组件内部会把src转成{ src: yourSrc, type: 根据文件后缀推断 }的形式合并进最终的 options.sources。两者都传时顶层src会覆盖 options 里的 sources。这里有一个实际经验如果你的视频源不是 mp4而是 HLSm3u8或者 DASHmpd格式强烈建议用 options.sources 明确指定 type不要依赖组件自动推断。例如const playerOptions { sources: [ { src: https://example.com/live/stream.m3u8, type: application/x-mpegURL } ] }因为组件按文件后缀推断 type 时对.m3u8的处理不一定符合 HLS 播放器的要求。显式指定type: application/x-mpegURL是最稳妥的。5. 访问播放器实例与事件监听掌控播放器的真正钥匙很多人的需求不只是放个视频而是要响应播放进度、上传学习记录、在某个时间点做弹窗或者拿到 player 实例调用 API。这一节讲清楚。5.1 通过 ref 获取 playervideojs-player/vue 在组件挂载完成后会把 Video.js 的 player 实例传给ready事件同时我们也给组件加一个reftemplate VideoPlayer refvideoPlayerRef :srcvideoSrc readyonPlayerReady / /template script setup import { ref } from vue import { VideoPlayer } from videojs-player/vue const videoPlayerRef ref(null) let player null const onPlayerReady (payload) { // payload 就是 Video.js 的 player 实例 player payload console.log(player.currentTime()) } /script拿到 player 实例之后Video.js 的几乎所有 API 都可以直接用player.play() // 播放 player.pause() // 暂停 player.currentTime(30) // 跳转到第 30 秒 player.playbackRate(1.5) // 设置倍速 player.volume(0.5) // 设置音量 player.src({ src: 新地址, type: video/mp4 }) // 动态换源 player.reset() // 重置播放器状态这里有一个 5.2 的坑ready事件的触发时机。由于 Video.js 的初始化是异步的如果你在 Vue 组件的onMounted里立刻通过videoPlayerRef.value去拿什么通常会拿到null或者未完全初始化的实例。正确的方式是在ready回调里操作或者用一个标记 ref 记录是否 ready再在需要的地方等待。const isReady ref(false) const onPlayerReady (playerInstance) { isReady.value true player playerInstance } // 在某个按钮点击事件里使用 const jumpTo (time) { if (isReady.value) { player.currentTime(time) } }5.2 事件监听组件事件 vs player 事件组件本身也暴露了很多事件直接写在模板上即可VideoPlayer play...一些逻辑 pause...另外一些逻辑 /但更复杂的事件比如timeupdate播放进度更新和loadedmetadata元数据加载完成在模板上监听也没问题。下面是完整的写法template VideoPlayer timeupdate(currentTime, event) handleTimeUpdate(currentTime, event) loadedmetadataonLoadedMetaData / /template script setup function handleTimeUpdate(currentTime, event) { console.log(当前播放时间, currentTime) } function onLoadedMetaData(payload) { const { player, duration } payload console.log(视频总时长, duration) } /script注意这里timeupdate事件的第一个参数是当前播放时间秒第二个才是原生 event。文档里写得很清楚但新手容易当成原生事件直接取event.target.currentTime。我的建议高频事件如 timeupdate里的逻辑一定要轻量。如果你想在 timeupdate 里上报学习进度建议做一个节流比如每 5 秒上报一次而不是每一帧都发请求let lastTime 0 const onTimeUpdate (currentTime) { if (currentTime - lastTime 5) { lastTime currentTime reportProgress(currentTime) // 上报请求 } }5.3 一些常用事件速查表事件名说明ready播放器初始化完成返回 player 实例play开始播放时触发pause暂停时触发ended播放结束时触发timeupdate播放进度更新约 250ms 触发一次loadedmetadata视频元数据时长、宽高等加载完成waiting视频缓冲等待时触发playing从缓冲状态恢复播放时触发fullscreenchange全屏状态变化时触发error播放错误时触发volumechange音量变化时触发5.4 动态切换视频源在后台管理系统里切换视频是最常见的操作。比如课程列表点一个视频播放器播另一个。template div button clickchangeVideo(https://vjs.zencdn.net/v/oceans.mp4)视频1/button button clickchangeVideo(https://vjs.zencdn.net/v/elephants-dream.mp4)视频2/button VideoPlayer :srcvideoSrc readyonReady / /div /template script setup import { ref } from vue const videoSrc ref(https://vjs.zencdn.net/v/oceans.mp4) let player null const onReady (instance) { player instance } const changeVideo (src) { videoSrc.value src // 如果你需要自动播放、从头开始播放 if (player) { player.currentTime(0) player.play() } } /script在这里videoSrc的响应式变化会驱动组件内部更新组件会调用 Video.js 的src()方法换源。但换源之后视频会不会从头播放取决于 Video.js 的配置和组件无关。如果你希望换源后立即从头播需要自己调用player.currentTime(0)和player.play()。另外有一个很实用的技巧切换 src 后海报图最好也同步更新。你可以使用posterprop 绑定响应式数据。6. 响应式数据驱动的几种绑定方式别让播放器和数据脱节Vue3 最大的特性就是响应式但 Video.js 是在内部维护了一套自己的状态。如何让这两套状态对齐是实际开发中最容易出问题的点。6.1 用 computed 动态生成播放器配置当播放器配置和组件内部的业务数据强相关时建议用 computed 来生成配置const currentLesson ref({ videoUrl: xxx.mp4, poster: xxx.jpg, title: 第 1 课 }) const playerOptions computed(() { return { sources: [ { src: currentLesson.value.videoUrl, type: video/mp4 } ], poster: currentLesson.value.poster, language: zh-CN } })VideoPlayer :optionsplayerOptions /这样每次currentLesson变化时组件会收到新的 options 对象并执行对应的更新逻辑。注意options 和 src 同时变化时src 的优先级更高所以二选一即可不要既传 src 又传 options.6.2 响应式属性 vs 直接调用 player 方法我见过不少开发者在代码里纠结到底是用响应式绑定控制播放还是直接调 player 方法我的建议是有一个简单的判断标准用户看得到的状态用响应式绑定比如:mutedisMuted、:srcvideoSrc瞬时的动作直接调方法比如 play、pause、seek。一个典型的错误是把play/pause做成响应式数据VideoPlayer :isPlayisPlaying /videojs-player/vue 确实提供了isPlay这个 prop但它更偏向于控制初始状态。如果你把播放状态做成受控的即完全由外部数据控制播放和暂停会出现一些时序问题——比如用户手动点暂停后isPlaying仍然是true下次数据一变播放器又自动播放了体验非常诡异。所以我的做法是播放暂停这种高频瞬时操作用 ref 事件回调来同步状态播放地址、静音、倍速等配置用响应式 props。这样可以避免受控/非受控混乱的问题。6.3 监听视频进度并同步到 UI在后台管理系统里经常需要做一个当前播放到第几分钟的组件外 UI。比如template div VideoPlayer :srcvideoSrc timeupdateonTimeUpdate durationchangeonDurationChange / div classprogress-info 当前进度{{ formattedCurrent }} / {{ formattedDuration }} /div /div /template script setup import { ref, computed } from vue const currentTime ref(0) const duration ref(0) const onTimeUpdate (time) { currentTime.value time } const onDurationChange ({ duration: d }) { duration.value d } const formattedCurrent computed(() formatTime(currentTime.value)) const formattedDuration computed(() formatTime(duration.value)) function formatTime(seconds) { const m Math.floor(seconds / 60) const s Math.floor(seconds % 60) return ${String(m).padStart(2, 0)}:${String(s).padStart(2, 0)} } /script这里用了durationchange事件来获取总时长。注意组件事件的第一个参数是随事件变化的durationchange的第一个参数是一个 payload 对象我习惯结构出 duration。每个事件参数结构略有不同建议用的时候打印一次确认别盲写。7. Vite / TypeScript / 后台管理系统里实际踩坑记录理论上这一节应该叫常见问题但我更想把真实项目里遇到过的、让我挠头的事情具体列出来可能更有参考价值。7.1 Vite 构建时 video.js 样式加载失败在 Vite 项目中引入video.js/dist/video-js.css本身没问题但如果你用的是非标准路径或者二次封装的库偶尔会遇到构建警告Failed to parse source map from ...这个警告通常不影响功能只是 source map 解析不出来。可以忽略。如果你的项目要求零警告可以在vite.config.js里配置// vite.config.js export default defineConfig({ build: { sourcemap: false } })但更实际地说大多数冲突来自样式顺序。如果你在组件里import video.js/dist/video-js.css之后又引入了其他 UI 库的全局样式播放器的部分样式可能被覆盖。建议把 video-js.css 放在全局样式文件的更靠前位置或者确保播放器容器内不使用 UI 库的全局 reset 类。7.2 TypeScript 下的类型推导videojs-player/vue 自带类型声明用起来还算舒服。在script setup中import { VideoPlayer } from videojs-player/vue import type { VideoPlayerReadyEvent } from videojs-player/vue事件参数的类型需要具体查看。如果不确定我习惯先不写类型鼠标悬停在 vscode 里看它推导出来的类型再回填。这在 TS 项目里非常实用不需要每次都翻文档。如果确实遇到类型问题比如某个事件报错可以做一个简单的声明const onPlayerReady (payload: any) { // 这里 any 也不是不行但建议拿到实例后转为 Player 类型 const player payload as Player }需要安装types/video.js才能用Player类型npm install -D types/video.js这里我提醒一句技术债要尽早还。用any虽然一时省事但当你的播放器生命周期函数多了之后IDE 就完全失去代码提示了你会被各种拼写错误反复折磨。7.3 后台管理系统中的路由缓存问题后台管理系统几乎都会用到 keep-alive 缓存页面比如从列表页进入详情页返回时希望保留列表状态。但这和视频播放器有一个隐蔽的冲突keep-alive 缓存的组件不会重新执行 onMounted而播放器依赖 DOM 挂载完成才能初始化。如果你把 VideoPlayer 放在一个被 keep-alive 缓存的页面里路由切换后回来播放器可能已经死了表现为黑屏、不能播放、控制条没有反应。我的解决办法有两个。方案一不缓存播放器所在页面。在路由配置里给播放器页面设置meta: { keepAlive: false }并在 keep-alive 上判断router-view v-slot{ Component } keep-alive :includecachedViews component :isComponent / /keep-alive /router-view方案二在 onActivated 生命周期里重新初始化。如果你的播放器确实必须处于缓存组件中可以在onActivated钩子里调用组件实例重新初始化但建议直接销毁重建import { nextTick, ref } from vue const showPlayer ref(true) onActivated(async () { // 销毁后重新创建播放器组件 showPlayer.value false await nextTick() showPlayer.value true })实战中我们要尽量避免这个场景。后台管理系统中视频播放页通常不需要缓存——你从课程列表进入播放页用户返回列表后再回到播放页大概率是期望从头重新加载列表而不是恢复上一次的视频进度。7.4 打包体积过大引入 Video.js 和 videojs-player/vue 之后bundle 体积会明显增加不少。在后台管理系统首屏优化时这一步经常被忽略。解决方案是路由级别代码分割const routes [ { path: /video, component: () import(/views/VideoPage.vue) // 播放器只在这里引用 } ]把播放器组件放到某个页面级组件里并用动态 import 加载路由播放器的 JS 和 CSS 会自动拆成一个独立的 chunk只有在进入该路由时才加载。这是最省事的优化方案。7.5 跨域问题视频源不允许跨域这个问题在开发环境最常见。你在本地跑 Vue 项目http://localhost:5173视频源在另一个域名下或者 CDN 上控制台会报 CORS 错误。Video.js 的很多能力比如 canvas 截图、运行时分析依赖视频资源支持跨域。遇到 CORS 报错时有三个方向排查确认视频源服务端配置了Access-Control-Allow-Origin: *或允许你的域名给 VideoPlayer 组件传crossoriginanonymous属性如果是开发环境且视频 URL 是相对路径确认 vite 的 proxy 配置没有覆盖到视频地址。其中第三条最坑——我在实际项目中遇到过一次视频 URL 恰好以/api开头结果被 vite 开发代理转发到了后端服务而不是 CDN于是返回了个 HTML 页面播放器直接报错。8. HLS 直播流与多清晰度切换的实战整合到了这个章节才算真正进入进阶领域。开发后台系统的同学可能很少接触到直播流但做在线教育、展会直播、大屏监控这类项目时HLS 播放是刚需。8.1 用视频js-contrib-hls还是用hls.js先澄清一个概念Video.js 本身不支持 HLS 流媒体播放。要在 Video.js 里播 m3u8 格式需要额外引入 HLS 支持。Video.js 官方维护了一个videojs/http-streamingVHS库负责在 Video.js 8.x 中处理 HLS。好消息是Video.js 8.x 已经默认集成了 VHS所以如果你安装的是最新版 video.js不需要额外配置就能播放 m3u8。如果你用的是 video.js 7.x则需要额外引入npm install videojs/http-streamingimport videojs/http-streaming不过实际项目中很多团队采用的是 HLS 主流的策略是使用hls.js库然后在 Video.js 中通过配置video.js-contrib-hls或者使用 VHS。这里我很直接地说在 Vue3 项目里直接用 video.js 8.x VHS 最省事因为不用额外引入 hls.js也不用处理 hls.js 和 video.js 的事件通道。下面是一个简单的 HLS 播放示例template VideoPlayer :optionshlsOptions / /template script setup import { VideoPlayer } from videojs-player/vue const hlsOptions { controls: true, language: zh-CN, sources: [ { src: https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8, type: application/x-mpegURL } ] } /script这就够了。video.js 8.x 会自动侦测 m3u8 地址调用内置的 VHS 进行播放。8.2 多清晰度切换定义多个 source 并动态切换在线教育里标清/高清/超清切换是非常常见的需求。实现思路并不复杂就是在 options 里预置多个清晰度供用户选择时调用player.src()切换。const resolutions { 标清: { src: https://example.com/video/720p.mp4, type: video/mp4 }, 高清: { src: https://example.com/video/1080p.mp4, type: video/mp4 }, 超清: { src: https://example.com/video/4k.mp4, type: video/mp4 } } const currentRes ref(标清) const switchResolution (quality) { if (quality currentRes.value) return const currentTime player.currentTime() player.src(resolutions[quality]) player.ready(() { player.currentTime(currentTime) player.play() }) }这里的一个经验是切换清晰度时先记录当前播放时间换源后 restore 播放时间避免让用户从头看。如果视频是 HLS 的Video.js 底层通常都有自适应码率机制你也可以手动控制。在配置里加const hlsOptions { html5: { vhs: { overrideNative: true, enableLowInitialPlaylist: true } } }8.3 直播场景自动跟随直播流、无进度条如果是直播流建议把控制条上的进度条隐藏或禁用否则用户拖动进度条没有意义。你可以在 CSS 里覆盖.video-js .vjs-progress-control { display: none; }另外直播场景中ended事件要小心处理——直播流一般没有播放结束这个事件几乎不会触发不要依赖它做业务逻辑。9. 视觉定制想让播放器融入你项目的 UI 风格Video.js 的默认皮肤质感不错但到了实际项目中很少有人能直接接受深蓝色高亮的默认 UI。好在 Video.js 的皮肤是通过 CSS 变量 类名覆盖实现的改起来比较直接。9.1 用 CSS 变量调整全局配色Video.js 8.x 使用了--vjs-*系列 CSS 变量来控制主要颜色。最常用的几个.video-js { /* 主色调 */ --vjs-theme-primary: #409eff; /* 控件聚焦时的颜色 */ --vjs-focus-outline-color: #409eff; /* 控制条背景 */ --vjs-control-bar-bg: rgba(0, 0, 0, 0.7); }把--vjs-theme-primary改成你们项目的品牌色播放器高亮部分会跟着变。实测下来进度条已播放部分、音量条、按钮聚焦颜色都会受影响。9.2 覆盖组件内部的类名有时候光改主色不够还要改具体控件样式。可以用深一点的类名选择器.video-js .vjs-play-progress { background-color: #ff9900; } .video-js .vjs-volume-level { background-color: #ff9900; } .video-js .vjs-big-play-button { background-color: rgba(255, 153, 0, 0.8); border: none; border-radius: 50%; width: 64px; height: 64px; line-height: 64px; }这里的大播放按钮big play button是很多项目必改的——默认是一个圆角矩形改成圆形会更符合现在的主流设计审美。9.3 自定义控制条按钮挂载你自己的图标如果你需要在控制条上增加一个自定义按钮比如截图弹幕开关可以用player.getChild(controlBar)添加。const onPlayerReady (playerInstance) { const controlBar playerInstance.getChild(controlBar) const screenshotButton playerInstance.controlBar.addChild(button, { controlText: 截图, className: vjs-screenshot-button }) screenshotButton.on(click, () { // 截图逻辑 const canvas document.createElement(canvas) // ... }) }这个 API 在 videojs-player/vue 里同样可用因为拿到的是原生 player 实例。不过这里确实绕过了 Vue 的响应式体系按钮上的文字要用controlText设置。9.4 加载动画与错误提示定制的坑播放器在缓冲时会显示一个 loading 动画。默认是转圈想改成文字提示加载中...可以直接在配置里设置const playerOptions { loadingSpinner: false, textTrackSettings: { // ... } }但更简单的是在 DOM 层面做用 CSS 隐藏默认 spinner然后在容器上加自己的 loading 元素。.video-js .vjs-loading-spinner { display: none; }可以给外层套一个 div根据播放状态用 v-if 控制显示自己的 loading 组件div classplayer-wrapper VideoPlayer :srcvideoSrc waitingloading true playingloading false / div v-ifloading classcustom-loading加载中.../div /div这个方法也是我在做在线教育后台时常用的方式因为默认的转圈 loading 在深色背景上不明显换成品牌色的 loading 组件体验更好。10. 一些来自实践的额外建议最后这一节我不做总结了就说几个我实际开发中总结出来的顺手技巧。技巧一统一封装一个视频播放器业务组件。不要在十几个页面里直接毫无包装地使用 VideoPlayer。建议在项目内部再包一层BaseVideoPlayer.vue把常用的 options、视频源、上报逻辑都集中起来。这样如果哪天 Video.js 升级导致 API 变化你只需要改一个文件。!-- BaseVideoPlayer.vue -- template VideoPlayer :srcsource :optionsmergedOptions v-bind$attrs readyemitReady timeupdateonTimeUpdate / /template script setup import { computed } from vue import { VideoPlayer } from videojs-player/vue const props defineProps({ source: { type: String, required: true }, poster: String, // ... }) const mergedOptions computed(() ({ language: zh-CN, playbackRates: [0.5, 1, 1.5, 2], poster: props.poster, // 你的默认配置 })) /script技巧二封面图非常影响用户对播放器是否加载成功的第一印象。很多后台在视频加载慢的时候用户看到黑屏会一直点播放按钮。设置一个好看的poster封面图哪怕视频没加载出来用户也知道这是一个待播放的视频。封面图不要用视频第一帧因为第一帧大概率是纯黑或模糊的。技巧三监控 error 事件并上报。如果视频源是客户提供的尤其是一些政务、教育客户时不时会遇到转码问题、防盗链问题、域名白名单问题。在 error 事件里把错误信息收集起来上报可以省去大量排查时间。const onError (payload) { console.error(播放错误, payload) // 上报逻辑 // reportError({ type: video, code: payload?.code, message: payload?.message }) }技巧四不要在 SSR / Nuxt 项目里直接引入该组件。videojs-player/vue 依赖 window / document 等浏览器 APINuxt 服务端渲染时会直接白屏或报错。如果要在 Nuxt 中用需要把播放器组件挂在一个 client-only 的包装里或者用dynamic import且关闭 ssr。这个限制不算独特几乎所有的播放器库都一样但确实值得提前知道。技巧五多视频页面注意实例销毁。如果你的页面里用 v-for 渲染多个播放器组件销毁时要确保每个 player 都执行了 dispose否则会留下大量 DOM 事件监听造成内存泄漏。videojs-player/vue 在组件卸载时会自动调用player.dispose()但你如果通过 ref 拿了一堆 player 实例最好也手动做清理onBeforeUnmount(() { player?.dispose() player null })
上一篇/下一篇内容由系统自动关联 返回资讯列表 →