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.
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,controlseposternão exige biblioteca alguma. - No Vue 3 com
<script setup>, capture o elemento com umref()de template e chame a API de mídia nativa emvideoRef.valuedentro deonMounted, nunca viadocument.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()emonBeforeUnmount(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
mutedquantoautoplay, e adicionarplaysinlinepara 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.
| Abordagem | Dependências extras | Esforço de UI personalizada | HLS / legendas | Escolha quando |
|---|---|---|---|---|
<video> nativo | Nenhuma | Você constrói cada controle | Apenas HLS nativo (Safari) | Clipe simples, bundle mínimo |
| Componente wrapper personalizado | Nenhuma | Controle total via slots | Apenas HLS nativo | UI sob medida, reutilizada na aplicação |
Video.js / @videojs-player/vue | video.js (+ wrapper) | Skin ou sobrescrita | Sim, nativo | Amplitude de formatos, streaming, tracks |
Discover how at OpenReplay.com.
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 deaddEventListeneremonUnmounted, 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
mutedquantoautoplay, conforme a política de autoplay do Chrome. playsinlinepara iOS. Sem o atributoplaysinline, 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 comdocument.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.
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