Vue 3 中优雅地集成 Video.js 播放器:从组件封装到功能定制

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() 销毁播放器实例,避免内存泄漏。
  • 暴露实例 :通过 defineExposeplayer 实例暴露给父组件,方便父组件直接调用播放器方法(如播放、暂停等)。

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 中重复定义 sourcesposter,因为这些已经在组件的 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 丰富的事件系统和插件机制也能很好地满足需求。

相关推荐
似水明俊德2 小时前
02-C#.Net-反射-学习笔记
开发语言·笔记·学习·c#·.net
蓝冰凌2 小时前
Vue 3 中 defineExpose 的行为【defineExpose暴露ref变量】详解:自动解包、响应性与实际使用
前端·javascript·vue.js
奔跑的呱呱牛2 小时前
generate-route-vue基于文件系统的 Vue Router 动态路由生成工具
前端·javascript·vue.js
于先生吖2 小时前
Java框架开发短剧漫剧系统:后台管理与接口开发
java·开发语言
柳杉2 小时前
从动漫水面到赛博飞船:这位开发者的Three.js作品太惊艳了
前端·javascript·数据可视化
khddvbe2 小时前
C++并发编程中的死锁避免
开发语言·c++·算法
wWYy.3 小时前
STL:list
开发语言·c++
TON_G-T3 小时前
day.js和 Moment.js
开发语言·javascript·ecmascript