12k
All articles

Como Adicionar Reprodução de Vídeo em Aplicações Vue

Adicione reprodução de vídeo a apps Vue com vídeo HTML5 nativo, componentes reutilizáveis ou Video.js para HLS, legendas, autoplay e limpeza.

OpenReplay Team
OpenReplay Team
Como Adicionar Reprodução de Vídeo em Aplicações Vue

Existem três maneiras de adicionar reprodução de vídeo a uma aplicação Vue, e a escolha certa depende de quanto controle você precisa: use o elemento nativo HTML5 <video> para um clipe incorporado simples, recorra ao Video.js quando precisar de streaming adaptativo, legendas ou amplo suporte a formatos, e utilize um componente pronto como @videojs-player/vue quando quiser os recursos do Video.js sem precisar configurar o player manualmente.

O clipe geralmente funciona bem na máquina em que você o desenvolveu. Aí um testador abre a página num iPhone e o vídeo pula para tela cheia, ou o autoplay simplesmente não faz nada no Chrome e ninguém entende o porquê. Por isso, este guia percorre as três abordagens com código Vue 3 <script setup> pronto para copiar e colar, e aborda as falhas que só aparecem no dispositivo de outra pessoa.

Principais Conclusões

  • Para um clipe incorporado simples com controles padrão, o elemento nativo HTML5 <video> vinculado com :src, controls e poster não exige biblioteca alguma.
  • No Vue 3 com <script setup>, capture o elemento com um ref() de template e chame a API de mídia nativa em videoRef.value dentro de onMounted, nunca via document.querySelector.
  • O Video.js é renderizado sem estilos a menos que você importe video.js/dist/video-js.css — o motivo mais comum para um player Video.js no Vue parecer quebrado.
  • Sempre que inicializar o Video.js, chame player.dispose() em onBeforeUnmount (Vue 3). Ignorar isso causa vazamento do player e do seu DOM a cada desmontagem.
  • Os navegadores bloqueiam autoplay com som: para reproduzir automaticamente de forma confiável, você deve definir tanto muted quanto autoplay, e adicionar playsinline para que o Safari no iOS reproduza inline em vez de forçar tela cheia.

Como escolher a abordagem de player de vídeo no Vue?

Escolha o <video> nativo quando não precisar de UI personalizada ou streaming adaptativo; escolha um wrapper personalizado quando quiser controles sob medida com total propriedade do código; escolha o Video.js ou seu wrapper em componente quando precisar de HLS, legendas ou muitos formatos. São três critérios que decidem: você precisa de UI personalizada, precisa de streaming adaptativo (HLS/DASH) e quantas dependências consegue tolerar.

AbordagemDependências extrasEsforço de UI personalizadaHLS / legendasEscolha quando
<video> nativoNenhumaVocê constrói cada controleApenas HLS nativo (Safari)Clipe simples, bundle mínimo
Componente wrapper personalizadoNenhumaControle total via slotsApenas HLS nativoUI sob medida, reutilizada na aplicação
Video.js / @videojs-player/vuevideo.js (+ wrapper)Skin ou sobrescritaSim, nativoAmplitude de formatos, streaming, tracks

HTML5 <video> nativo no Vue (comece por aqui)

A maneira mais rápida de adicionar reprodução de vídeo no Vue é um elemento <video> nativo com propriedades vinculadas via :src, poster e preload, mais um ref() de template para chamar a API HTMLMediaElement: play(), pause() e .muted. No Vue 3 com <script setup>, vincule eventos de mídia nativos como timeupdate, loadedmetadata e ended diretamente no template, para que o Vue anexe e remova os listeners por você.

<script setup>
import { ref } from 'vue'

const videoRef = ref(null)
const playing = ref(false)
const muted = ref(false)
const current = ref(0)
const duration = ref(0)

function togglePlay() {
  const el = videoRef.value
  el.paused ? el.play() : el.pause()
}

function toggleMute() {
  const el = videoRef.value
  el.muted = !el.muted
  muted.value = el.muted
}
</script>

<template>
  <video
    ref="videoRef"
    src="/media/clip.mp4"
    poster="/media/poster.jpg"
    preload="metadata"
    playsinline
    @play="playing = true"
    @pause="playing = false"
    @ended="playing = false"
    @loadedmetadata="duration = $event.target.duration"
    @timeupdate="current = $event.target.currentTime"
  />
  <div>
    <button @click="togglePlay">{{ playing ? 'Pause' : 'Play' }}</button>
    <button @click="toggleMute">{{ muted ? 'Unmute' : 'Mute' }}</button>
    <span>{{ current.toFixed(0) }}s / {{ duration.toFixed(0) }}s</span>
  </div>
</template>

O ref="videoRef" do template resolve para o elemento DOM em videoRef.value. Acesse-o dentro de handlers ou de onMounted, nunca antes de o componente ser montado. Na Options API, os equivalentes são this.$refs.videoRef e o hook mounted. Se você anexar listeners manualmente com addEventListener, remova-os em onUnmounted; vincular através de @event no template evita completamente essa classe de vazamento.

Componente de player personalizado reutilizável

Para reutilizar a lógica do player em toda a aplicação, encapsule o <video> nativo em um componente que exponha seus controles e estado através de um scoped slot, de modo que cada uso componha seus próprios botões e barra de progresso sem duplicar a lógica de reprodução.

<!-- VideoPlayer.vue -->
<script setup>
import { ref } from 'vue'
defineProps({ src: String, poster: String })
const emit = defineEmits(['timeupdate', 'ended'])

const videoRef = ref(null)
const playing = ref(false)

function togglePlay() {
  const el = videoRef.value
  el.paused ? el.play() : el.pause()
}
</script>

<template>
  <video
    ref="videoRef"
    :src="src"
    :poster="poster"
    playsinline
    @play="playing = true"
    @pause="playing = false"
    @timeupdate="emit('timeupdate', $event.target.currentTime)"
    @ended="emit('ended')"
  />
  <slot name="controls" :playing="playing" :toggle-play="togglePlay" />
</template>

Os consumidores extraem playing e togglePlay das props do slot e renderizam qualquer UI de que precisem. Um único player base pode sustentar tanto um embed minimalista apenas com play quanto um player completo com barra de progresso, mantendo a lógica de mídia em um só lugar.

<VideoPlayer src="/media/clip.mp4" @ended="onEnded">
  <template #controls="{ playing, togglePlay }">
    <button @click="togglePlay">{{ playing ? 'Pause' : 'Play' }}</button>
  </template>
</VideoPlayer>

Quando você deve usar o Video.js?

Use o Video.js quando precisar de HLS/streaming adaptativo, legendas e text tracks, um visual consistente ou amplitude de formatos além do que o <video> nativo garante. Instale video.js, renderize um <video class="video-js">, inicialize videojs(ref, options) em onMounted, importe a folha de estilos e destrua o player na desmontagem.

<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import videojs from 'video.js'
import 'video.js/dist/video-js.css' // required, or the player renders unstyled

const videoRef = ref(null)
let player = null

const options = {
  autoplay: false,
  controls: true,
  preload: 'auto',
  fluid: true,
  sources: [{ src: '/media/clip.mp4', type: 'video/mp4' }]
}

onMounted(() => {
  player = videojs(videoRef.value, options)
})

onBeforeUnmount(() => {
  if (player) player.dispose()
})
</script>

<template>
  <video ref="videoRef" class="video-js" playsinline />
</template>

Duas coisas são fáceis de errar aqui: o nome do ref no template deve corresponder ao que você lê em onMounted (um par incompatível videoJsPlayer/videoPlayer lança erro), e dispose() pertence a onBeforeUnmount no Vue 3. beforeDestroy é um hook do Vue 2, e o Vue 2 chegou ao fim de vida em 31 de dezembro de 2023. A linha estável atual é o Video.js 8.x, com a versão 8.24.0 lançada em agosto de 2026. Um Video.js 10 modular está em beta e ainda não disponível de forma geral: o cronograma do repositório v10 ainda lista a disponibilidade geral como trabalho em andamento, com a paridade do core do Video.js e dos contribs prevista para o final de 2026. Permaneça no 8.x em produção e mantenha a instalação sem versão fixada: npm install video.js.

Componente pronto: @videojs-player/vue

O caminho com menos código para usar Video.js no Vue 3 é o @videojs-player/vue, um <video-player> plug-and-play com props reativas (src, sources, poster, controls, loop, volume, fluid, playsinline, tracks) e um scoped slot { player, state } para controles personalizados. Ele cuida da inicialização e da destruição internamente, então um clipe HLS se resume a trocar a fonte em uma única linha.

<script setup>
import { VideoPlayer } from '@videojs-player/vue'
import 'video.js/dist/video-js.css'
</script>

<template>
  <video-player
    src="https://example.com/stream/playlist.m3u8"
    poster="/media/poster.jpg"
    controls
    fluid
    playsinline
    :volume="0.6"
    @ready="(payload) => console.log(payload.state)"
  >
    <template #default="{ player, state }">
      <button @click="state.playing ? player.pause() : player.play()">
        {{ state.playing ? 'Pause' : 'Play' }}
      </button>
    </template>
  </video-player>
</template>

Uma ressalva importante antes de adotá-lo: este é o wrapper padrão para Vue 3, mas seu lançamento no npm estagnou na v1.0.0, publicada em 2022, e os scanners de dependências o sinalizam como pouco mantido. Sua faixa publicada de peer dependencies ainda exige video.js 7.x, então combiná-lo com o Video.js 8 gera um aviso de peer dependency no momento da instalação. Se você estiver no Vue 2, use o build legado vue-video-player, referenciado na seção de legado do repositório. As versões atuais do @videojs-player/vue têm como alvo apenas o Vue 3.

Checklist de armadilhas específicas do Vue

A maioria dos bugs de “o vídeo não reproduz” no Vue tem origem em um punhado de regras de plataforma que passam despercebidas na revisão de código, mas falham em dispositivos reais. Falhas de autoplay e playsinline, em particular, são invisíveis até que um usuário as encontre — exatamente a classe de problema específico de dispositivo que o session replay revela a partir de sessões reais.

  • Limpe na desmontagem. Chame player.dispose() para o Video.js e remova quaisquer handlers manuais de addEventListener em onUnmounted, ou cada ciclo de montagem/desmontagem causará vazamento.
  • Importe o CSS. import 'video.js/dist/video-js.css' ou o player ficará sem estilos.
  • Autoplay mudo é obrigatório. Os navegadores bloqueiam autoplay com som; defina tanto muted quanto autoplay, conforme a política de autoplay do Chrome.
  • playsinline para iOS. Sem o atributo playsinline, o Safari no iPhone geralmente abrirá o vídeo em tela cheia; o iPad se comporta de forma diferente.
  • Use template refs, não consultas ao DOM. Alcance o elemento através de ref(), nunca com document.querySelector.

Comece pelo nativo: um <video> com bindings entrega reprodução em minutos e com zero dependências. Evolua para o Video.js ou o @videojs-player/vue no momento em que precisar de HLS, legendas ou uma UI consistente entre navegadores, e implemente a limpeza com dispose() desde o primeiro commit, para que ela nunca se torne um vazamento que você terá de caçar depois.

Perguntas Frequentes

Como reproduzo um stream HLS (.m3u8) em uma aplicação Vue?

O vídeo HTML5 nativo reproduz HLS apenas no Safari, então para HLS multiplataforma use o Video.js ou seu wrapper @videojs-player/vue, que inclui suporte a HLS através do engine videojs-http-streaming. Com o @videojs-player/vue você define o src para a URL da sua playlist .m3u8 e o componente gerencia o engine de streaming internamente. No Chrome ou Firefox puros, o vídeo nativo não tem HLS embutido, o que é o principal motivo para recorrer ao Video.js.

Por que o autoplay não funciona no meu player de vídeo Vue?

O autoplay mudo é geralmente permitido, mas o autoplay com som exige que o usuário tenha interagido antes com o site, e o Chrome também o permite quando o site tem uma pontuação alta no Media Engagement Index. O Safari aplica sua própria política semelhante. Para reproduzir automaticamente de forma confiável, você deve definir tanto muted quanto autoplay no elemento ou nas opções do Video.js. Essa falha é invisível na revisão de código porque depende da política do navegador e do dispositivo, não da sua marcação, então só se manifesta quando um usuário real carrega a página.

Qual é a diferença entre o @videojs-player/vue e usar o Video.js diretamente?

O @videojs-player/vue é um componente plug-and-play para Vue 3 que encapsula o Video.js, tratando a inicialização e a destruição internamente e expondo props reativas mais um scoped slot com player e state, ao passo que usar o Video.js diretamente significa renderizar um elemento video, chamar videojs() em onMounted e destruí-lo você mesmo em onBeforeUnmount. O wrapper exige menos código, mas seu lançamento no npm estagnou na v1.0.0 de 2022, então o Video.js direto lhe dá mais controle sobre versões e ciclo de vida.

Ainda posso usar o @videojs-player/vue com Vue 2?

Não. As versões atuais do @videojs-player/vue têm como alvo apenas o Vue 3. O pacote adotou seu nome atual quando o suporte ao React foi adicionado, e essa mudança foi uma breaking change para os usuários de Vue. Para Vue 2 você precisa do build mais antigo vue-video-player 5.x, que o repositório ainda referencia em sua seção de legado. O próprio Vue 2 chegou ao fim de vida em 31 de dezembro de 2023, então novos trabalhos devem ter como alvo o Vue 3.

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.