Vue 3 中优雅地集成 Video.js 播放器:从组件封装到功能定制
在现代 Web 开发中,视频播放是一个常见需求。Video.js 是一个强大且成熟的 HTML5 视频播放器库,它提供了丰富的 API 和可定制的 UI。当与 Vue 3 的组合式 API 结合时,我们可以轻松地封装一个可复用的视频播放器组件,并精细控制其行为(如禁用进度条拖动、隐藏不需要的控件等)。本文将通过一个完整的示例,展示如何在 Vue 3 项目中封装 Video.js 播放器,并在父组件中灵活配置。
下载依赖
npm install video.js
一、VideoPlayer 组件实现
1. 组件模板
首先,我们需要在模板中放置一个 <video> 标签,并为其添加 ref 属性以便在 JavaScript 中获取 DOM 元素。Video.js 会基于这个元素初始化播放器。
vue
<template>
<div class="video-player-container">
<video
ref="videoRef"
class="video-js vjs-default-skin vjs-big-play-centered"
controls
preload="auto"
:poster="poster"
>
<source :src="src" :type="type" />
<p class="vjs-no-js">
To view this video please enable JavaScript, and consider upgrading to a
web browser that
<a href="https://videojs.com/html5-video-support/" target="_blank">
supports HTML5 video
</a>
</p>
</video>
</div>
</template>
ref="videoRef":用于在setup中获取该 DOM 元素。class中添加了 Video.js 需要的默认类名,确保样式正常。controls属性启用原生控件,但 Video.js 会接管并渲染自己的控件。:poster动态绑定海报图。<source>标签指定视频源,同样使用动态属性。
2. 脚本部分(组合式 API)
接下来是组件的核心逻辑。我们将使用 Vue 3 的 <script setup> 语法,并引入 Video.js 及其样式。
vue
<script lang='ts' setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import videojs from 'video.js';
import 'video.js/dist/video-js.css';
defineOptions({ name: 'VideoPlayer', inheritAttrs: false });
const props = defineProps({
src: { type: String, required: true },
type: { type: String, default: 'video/mp4' },
poster: { type: String, default: '' },
options: { type: Object, default: () => ({}) },
});
const videoRef = ref(null);
let player = null;
const initPlayer = () => {
if (!videoRef.value) return;
// 默认配置
const defaultOptions = {
controls: true,
autoplay: false,
preload: 'auto',
fluid: true, // 自适应容器宽度
aspectRatio: '16:9', // 宽高比
playbackRates: [0.5, 1, 1.5, 2], // 倍速选项
sources: [{ src: props.src, type: props.type }],
poster: props.poster,
};
// 合并用户传入的 options
const finalOptions = { ...defaultOptions, ...props.options };
player = videojs(videoRef.value, finalOptions, function onPlayerReady() {
console.log('Video.js player ready', this);
// 调用进度条控件的 disable 方法禁用拖动和点击
this.controlBar.progressControl.disable();
});
};
const disposePlayer = () => {
if (player) {
player.dispose();
player = null;
}
};
// 暴露播放器实例给父组件(可选)
defineExpose({ player });
onMounted(() => {
initPlayer();
});
onBeforeUnmount(() => {
disposePlayer();
});
</script>
关键点说明
- 默认配置与合并 :我们定义了一套合理的默认选项(如
fluid: true使播放器自适应,playbackRates提供倍速切换),然后通过展开运算符与用户传入的options合并,让父组件可以按需覆盖或扩展。 - 进度条禁用 :在播放器
ready回调中,我们调用了this.controlBar.progressControl.disable()方法。这是 Video.js 提供的一种禁用进度条交互的方式,可以阻止用户拖动或点击进度条跳转。如果你希望保留进度条显示但完全禁止交互,这种方法很有效。 - 生命周期管理 :在
onMounted中初始化,在onBeforeUnmount中调用dispose()销毁播放器实例,避免内存泄漏。 - 暴露实例 :通过
defineExpose将player实例暴露给父组件,方便父组件直接调用播放器方法(如播放、暂停等)。
3. 样式
简单地为容器添加一些样式,限制最大宽度并居中。
vue
<style scoped>
.video-player-container {
width: 100%;
max-width: 800px;
margin: 0 auto;
}
</style>
二、父组件中使用 VideoPlayer
现在,我们可以在父组件中引入并使用 VideoPlayer,并传入自定义配置。
vue
<template>
<div>
<h1>我的视频播放器</h1>
<VideoPlayer
ref="videoPlayerRef"
src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-720p.mp4"
type="video/mp4"
poster="https://ms.bdimg.com/pacific/0/pic/-782555709_-1732160829.jpg"
:options="playerOptions"
/>
</div>
</template>
<script setup>
import { ref } from 'vue';
import VideoPlayer from './components/VideoPlayer.vue';
const videoPlayerRef = ref(null);
const playerOptions = {
autoplay: false,
controls: true,
controlBar: {
playbackRateMenuButton: false, // 隐藏倍速按钮
seekToLive: false, // 隐藏"跳转至直播"按钮(如果有)
volumePanel: { inline: false }, // 音量控制样式
},
html5: {
vhs: { overrideNative: true }, // HLS 配置
nativeControlsForTouch: false,
disablePictureInPicture: true, // 禁用画中画
},
};
// 如果需要调用播放器方法,可以通过 videoPlayerRef.value.player 访问
// 例如:videoPlayerRef.value.player.play()
</script>
配置解析
controlBar选项 :我们通过playbackRateMenuButton: false隐藏了倍速切换按钮;seekToLive: false隐藏直播场景下的"跳转至直播"按钮(非直播流无影响);volumePanel调整了音量控件的布局。html5选项 :disablePictureInPicture: true彻底禁用画中画功能,确保用户无法通过任何方式进入画中画模式。同时设置了 HLS 相关的vhs配置和nativeControlsForTouch。- 注意,我们并没有在
playerOptions中重复定义sources或poster,因为这些已经在组件的 props 中传递了。如果需要覆盖,也可以在options中传入,但要注意合并策略。
三、进阶功能与注意事项
1. 响应式更新视频源
如果父组件的 src 发生变化,我们需要通知播放器更新视频源。可以在 VideoPlayer 组件中添加一个 watch 监听 props.src,并调用 player.src() 方法更新。
javascript
watch(() => props.src, (newSrc) => {
if (player) {
player.src({ src: newSrc, type: props.type });
}
});
同样地,如果需要监听 poster 变化,可以使用 player.poster(newPoster)。
2. 彻底禁用进度条交互的备选方案
虽然 progressControl.disable() 在很多情况下有效,但某些 Video.js 版本可能不支持该方法。更稳健的做法是在 ready 回调中手动移除进度条的事件监听:
javascript
player.ready(function() {
const seekBar = this.controlBar.progressControl.seekBar;
seekBar.off('mousedown');
seekBar.off('touchstart');
seekBar.off('click');
});
这种方法保留了进度条的视觉显示,但阻止了任何鼠标或触摸交互。
3. 关于倍速播放
我们在默认选项中配置了 playbackRates: [0.5, 1, 1.5, 2],但在父组件的 options 中又通过 controlBar.playbackRateMenuButton: false 隐藏了倍速按钮。这样做的结果是:倍速功能依然存在(例如通过键盘快捷键或 API 调用),但用户无法通过 UI 操作。如果你希望完全禁止倍速,可以不配置 playbackRates 数组,或者监听 ratechange 事件强制重置速度为 1。
4. 移动端兼容性
对于 iOS 设备,需要注意 playsinline 属性,通常建议在 <video> 标签上添加 playsinline 以防止视频自动全屏。你可以通过 options 传递该属性:
javascript
const playerOptions = {
// ...其他选项
html5: {
playsinline: true,
// ...
}
};
5. 内存泄漏防范
务必在组件卸载时调用 player.dispose(),否则播放器实例会一直占用内存,且可能导致事件监听无法释放。
四、总结
通过以上封装,我们获得了一个灵活、可复用的 Vue 3 视频播放器组件。它支持:
- 通过 props 传入视频源、海报和自定义配置。
- 合并默认选项和用户选项,满足不同场景需求。
- 在播放器初始化后禁用进度条交互,防止用户快进。
- 通过
controlBar配置隐藏不需要的控件(如倍速、画中画按钮)。 - 正确管理生命周期,避免内存泄漏。
父组件可以轻松地使用该组件,并传递定制化的 options 来控制播放器的行为和外观。如果你需要进一步扩展(如添加自定义按钮、监听播放事件等),Video.js 丰富的事件系统和插件机制也能很好地满足需求。