Vue.js: Refs e DOM
Última atualização: 2026-08-26
As referências de modelo permitem que você acesse diretamente elementos do DOM ou instâncias de componentes filhos — por exemplo, chamando input.focus() ou acessando métodos de componentes. O Vue 3.5 introduziu a API de Composição useTemplateRef, mais poderosa, que funciona em conjunto com a inferência de tipos do TypeScript.
Os Template Refs são um “último recurso” — o Vue recomenda o uso de refs, reactive, props e emit para resolver a maioria dos problemas; os Template Refs só devem ser usados quando você precisar manipular diretamente o DOM ou uma instância de componente.
1. O que você vai aprender
ref="el"Noções básicas sobre referências a modelosuseTemplateRef(Recomendado para Vue 3.5+)- Acesso a elementos do DOM (foco, scrollIntoView, etc.)
- Acessando uma instância de um componente filho (usando o método
defineExpose) $refsSintaxe alternativa utilizando a API de composição- Matrizes de referência em
v-for - 5 principais casos de uso e 4 antipadrões
2. O dilema do “foco automático” em um formulário de login
(1) Problema: Como fazer com que o campo de entrada receba o foco automaticamente quando a janela modal for aberta?
Alice criou um modal de login que deveria colocar o foco automaticamente no campo de nome de usuário:
// ❌ The "Broken" Version:Directly querySelector
onMounted(() => {
const input = document.querySelector('.username-input')
input.focus() // ❌ Does not meet the requirements Vue Philosophy
})
A filosofia do Vue: evite a manipulação direta do DOM. Em vez disso, use Template Refs.
O gerente de produto Charlie:
“Alice, quando a janela modal abrir, o campo do nome de usuário deve receber o foco automaticamente para que os usuários possam começar a digitar imediatamente.”
(2) Solução para referências de modelos de visualização
<template>
<!-- ref="usernameInput" Mark this element -->
<input ref="usernameInput" type="text" class="username-input">
</template>
<script setup>
import { ref, onMounted } from 'vue'
// 1. Create ref Variable(The name must match the template ref Match)
const usernameInput = ref(null)
onMounted(() => {
// 2. DOM Ready,Visit input Element
usernameInput.value.focus()
})
</script>
<!-- Pop-up Scenarios:Click the button to open modal,Automatic focus -->
<template>
<button @click="showModal = true">Login</button>
<Modal v-if="showModal" @close="showModal = false">
<input ref="usernameInput" type="text">
</Modal>
</template>
<script setup>
import { ref, nextTick } from 'vue'
const showModal = ref(false)
const usernameInput = ref(null)
async function openModal() {
showModal.value = true
// ✅ Wait for DOM update, then focus
await nextTick()
usernameInput.value.focus()
}
</script>
(3) Receita
Após usar as referências de modelo:
- Legibilidade do código: Indique claramente “Quero referenciar este elemento”
- Está alinhado com a filosofia do Vue: não manipula diretamente o DOM
- Segurança de tipos: O TypeScript infere os tipos dos elementos
- Ciclo de vida correto: Acesso em
onMountedounextTick
3. Uso básico do ref
(1) Referência de string (estilo Vue 2, obsoleta)
// ❌ Not recommended:String ref
export default {
mounted() {
this.$refs.input.focus()
}
}
(2) A variável ref (recomendada no Vue 3)
<template>
<input ref="inputRef">
</template>
<script setup>
import { ref, onMounted } from 'vue'
const inputRef = ref(null)
onMounted(() => {
console.log(inputRef.value) // <input> DOM Element
inputRef.value.focus() // Call DOM API
})
</script>
(3) 5 Operações básicas
// 1. Visit DOM Element
inputRef.value // <input> Element
// 2. Call DOM API
inputRef.value.focus()
inputRef.value.blur()
inputRef.value.select()
inputRef.value.scrollIntoView()
// 3. Read/Edit DOM Properties
inputRef.value.value // input value
inputRef.value.disabled // disabled Properties
inputRef.value.style.color = 'red' // Edit Style
// 4. Monitoring DOM Events (Not recommended, use @event)
inputRef.value.addEventListener('focus', handler)
// 5. Accessing a Child Component Instance(For more details, see 17.4)
childRef.value.someMethod()
4. useTemplateRef (Recomendado para Vue 3.5+)
(1) Por que o useTemplateRef é necessário?
A variável ref em <script setup> requer dois nomes (o modelo ref e a variável), o que pode facilmente levar a inconsistências. useTemplateRef lida com isso usando um único nome, e a inferência de tipos do TypeScript é mais poderosa.
<template>
<input ref="usernameInput">
</template>
<script setup>
import { useTemplateRef, onMounted } from 'vue'
// ✅ One Name Does It All(Vue 3.5+)
const inputRef = useTemplateRef('usernameInput')
onMounted(() => {
inputRef.value.focus() // Type automatically inferred as HTMLInputElement
})
</script>
(2) 5 principais vantagens
| Vantagem | Descrição |
|---|---|
| Inferência de tipo | O TypeScript reconhece automaticamente como um HTMLInputElement |
| Renomeação segura | Altere um nome em um único lugar; o IDE sincroniza a alteração |
| Como evitar incompatibilidades de nomes | Ocorrem erros quando as referências de modelo e os nomes das variáveis não coincidem |
| Configuração simplificada | Não é necessário o const inputRef = ref(null) |
| DevTools aprimoradas | Suporte ao Vue DevTools 5.x |
(3) Comparação completa
<!-- Old notation:Variable ref -->
<template>
<input ref="usernameInput">
</template>
<script setup>
import { ref, onMounted } from 'vue'
const inputRef = ref(null) // The name may not match the template.
onMounted(() => inputRef.value.focus())
</script>
<!-- New Writing Style:useTemplateRef(Vue 3.5+ Recommendations)-->
<template>
<input ref="usernameInput">
</template>
<script setup>
import { useTemplateRef, onMounted } from 'vue'
const inputRef = useTemplateRef('usernameInput') // One Name Does It All
onMounted(() => inputRef.value.focus())
</script>
5. Acessando uma instância de um componente filho
(1) defineExpose: Método Expose
<!-- Child component:MyInput.vue -->
<template>
<input ref="inputRef" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)">
</template>
<script setup>
import { ref } from 'vue'
const props = defineProps({ modelValue: String })
const inputRef = ref(null)
// Exposed for use by the parent component
defineExpose({
focus: () => inputRef.value?.focus(),
select: () => inputRef.value?.select(),
clear: () => { inputRef.value.value = '' }
})
</script>
(2) Acesso ao componente pai
<template>
<MyInput ref="myInputRef" v-model="searchQuery" />
<button @click="focusInput">Focus Input</button>
</template>
<script setup>
import { ref } from 'vue'
import MyInput from './MyInput.vue'
const searchQuery = ref('')
const myInputRef = ref(null)
function focusInput() {
myInputRef.value.focus() // Calling Methods Exposed by Child Components
myInputRef.value.select() // You can also use chained calls
}
</script>
(3) 5 principais casos de uso
| Cenário | Exposto pelo componente filho | Chamado pelo componente pai |
|---|---|---|
| Destaque do formulário | focus() |
inputRef.focus() |
| Limpar formulário | clear() |
formRef.clear() |
| Atualizar dados | reload() |
tableRef.reload() |
| Abrir janela pop-up | open() |
modalRef.open() |
| Enviar formulário | submit() |
formRef.submit() |
6. A matriz ref em v-for
(1) Uso básico
<template>
<ul>
<!-- In v-for, ref auto-collected into array -->
<li v-for="item in items" :key="item.id" ref="itemRefs">
{{ item.name }}
</li>
</ul>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const items = ref([
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Banana' },
{ id: 3, name: 'Cherry' }
])
// ✅ Array Format
const itemRefs = ref([])
onMounted(() => {
// Page 2 Items DOM
itemRefs.value[1].style.color = 'red'
})
</script>
(2) Referência dinâmica (v-for com contagem dinâmica)
<template>
<button v-for="i in count" :key="i" :ref="el => buttonRefs[i] = el">
Button {{ i }}
</button>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const count = ref(3)
const buttonRefs = ref({})
onMounted(() => {
// buttonRefs[0] = 1st button
// buttonRefs[1] = 2nd button
console.log(buttonRefs.value[0])
})
</script>
(3) 5 pontos importantes a serem lembrados
| Pontos a serem observados | Descrição |
|---|---|
| Ordem da matriz | Corresponde à ordem dos dados no v-for |
| Reativo | Alterações na matriz ref exigem um observador |
| Renderização condicional | as referências podem não ser atualizadas após o v-if |
| Número de variáveis dinâmicas | Uso de referência a objeto ou função |
| Desempenho | Um grande número de referências (mais de 100) pode tornar a renderização mais lenta |
7. Exemplos completos: 5 cenários do mundo real
▶ Exemplo: 1. Foco automático no formulário de login
<template>
<form @submit.prevent="handleLogin">
<input ref="usernameRef" v-model="username" placeholder="Username">
<input ref="passwordRef" v-model="password" type="password" placeholder="Password">
<button>Login</button>
</form>
</template>
<script setup>
import { ref, onMounted, useTemplateRef } from 'vue'
// ❌ Old notation
const usernameRef = ref(null)
const passwordRef = ref(null)
// ✅ New Writing Style(Vue 3.5+)
// const usernameRef = useTemplateRef('usernameRef')
// const passwordRef = useTemplateRef('passwordRef')
const username = ref('')
const password = ref('')
onMounted(() => {
usernameRef.value.focus() // Autofocus Username
})
function handleLogin() {
console.log('Login:', username.value, password.value)
}
</script>
▶ Exemplo: 2. Rolagem automática até o final da página
<template>
<div ref="messagesRef" class="messages">
<div v-for="msg in messages" :key="msg.id">{{ msg.text }}</div>
</div>
<input v-model="newMessage" @keyup.enter="sendMessage">
<button @click="sendMessage">Send</button>
</template>
<script setup>
import { ref, nextTick } from 'vue'
const messages = ref([])
const newMessage = ref('')
const messagesRef = ref(null)
async function sendMessage() {
messages.value.push({ id: Date.now(), text: newMessage.value })
newMessage.value = ''
// ✅ Wait for DOM update, then scroll
await nextTick()
messagesRef.value.scrollTop = messagesRef.value.scrollHeight
}
</script>
▶ Exemplo: 3. Componente pai chamando um método de um componente filho
<!-- Child component:FormValidator.vue -->
<template>
<form>
<input v-model="email" placeholder="Email">
<input v-model="password" type="password" placeholder="Password">
</form>
</template>
<script setup>
import { ref } from 'vue'
const email = ref('')
const password = ref('')
const emailRef = ref(null)
const passwordRef = ref(null)
defineExpose({
validate: () => {
if (!email.value) {
emailRef.value.focus()
return false
}
if (!password.value || password.value.length < 6) {
passwordRef.value.focus()
return false
}
return true
},
reset: () => {
email.value = ''
password.value = ''
}
})
</script>
<!-- Parent Component:LoginPage.vue -->
<template>
<FormValidator ref="formRef" />
<button @click="submit">Submit</button>
</template>
<script setup>
import { ref } from 'vue'
import FormValidator from './FormValidator.vue'
const formRef = ref(null)
function submit() {
if (formRef.value.validate()) {
console.log('Valid!')
} else {
console.log('Invalid!')
}
}
</script>
▶ Exemplo: 4. Inferência de tipo para useTemplateRef
<template>
<input ref="usernameInput" type="text">
<MyChart ref="chartComponent" :data="chartData" />
</template>
<script setup lang="ts">
import { ref, useTemplateRef, onMounted } from 'vue'
import MyChart from './MyChart.vue'
// ✅ TypeScript Inference:HTMLInputElement | null
const usernameInput = useTemplateRef<HTMLInputElement>('usernameInput')
// ✅ TypeScript Inference:InstanceType<typeof MyChart> | null
const chartComponent = useTemplateRef<InstanceType<typeof MyChart>>('chartComponent')
onMounted(() => {
// usernameInput.value Automatic is HTMLInputElement
usernameInput.value?.focus()
// chartComponent.value "Auto" is a component instance
chartComponent.value?.refresh()
})
</script>
▶ Exemplo: 5. Referência rápida para 5 erros comuns
| Erro | Sintoma | Solução |
|---|---|---|
| Erro ortográfico na referência do modelo | o valor da referência é nulo | Verifique se os nomes das referências correspondem |
Acesso no nível superior setup |
ref.value é nulo |
Use onMounted |
| Acesso dentro de uma janela pop-up | Não funciona | Use nextTick ou métodos DOM semelhantes |
| Acesso à matriz de referência no v-for | Desalinhamento de índices | Use :key para preservar a ordem |
| Referência entre componentes | indefinido | Os componentes filhos usam defineExpose |
▶ Exemplo: 6. Comparação dos 5 principais tipos de referência
| Tipo | Exemplo | Aplicável a |
|---|---|---|
| Elemento DOM | ref="inputRef" → HTMLInputElement |
Acessar input/div |
| Instância do componente | ref="childRef" → Instância do componente |
Métodos do componente de tom |
| v-for array | ref="itemRefs" → Array |
Acessando itens da lista |
| Referência funcional | :ref="el => ..." → Elemento único |
Contagem dinâmica |
| Referência de string | ref="name" → this.$refs |
Estilo da Visualização 2 (não recomendado) |
❓ Perguntas Frequentes
P: Quando se deve usar Template Refs? R: Em quatro situações: (1) manipulação do DOM (foco/rolagem/canvas); (2) chamada de métodos de componentes; (3) integração de bibliotecas de terceiros (ECharts/Mapbox); (4) medição das dimensões dos elementos. Em outros cenários, use refs, reactive ou props.
P: O
useTemplateRefé obrigatório no Vue 3.5+? R: Sim. Ele só é compatível com o Vue 3.5+. No Vue 3.4 e versões anteriores, use a variávelref. É preferível usar a inferência de tipos do TypeScript.
P: Qual é a diferença entre Template Refs e
useRef(React)? R: OuseRefdo React retorna uma ref mutável, enquanto a template ref do Vue retorna um objeto ref. OuseTemplateRefdo Vue 3.5+ possui uma API mais próxima da douseRefdo React.
P: Quais métodos um componente filho deve expor? R: Exponha apenas os métodos de que o componente pai realmente precisa (como focus, clear e validate). Não exponha outros métodos internos (princípio da encapsulação).
P: Quando a matriz ref em
v-foré atualizada? R: Ela é atualizada sempre quev-foré renderizada novamente. Também é atualizada quandov-ifé ativada ou desativada. Você pode usarwatchna matriz ref para responder às alterações.
P: As Template Refs entram em conflito com o provide/inject? R: Não, não entram. As Template Refs são usadas para o “acesso de pai para filho”, enquanto o provide/inject é usado para o “compartilhamento de dados entre níveis”. Elas podem ser usadas em conjunto.
P: Como faço para adicionar um tipo a uma referência no TypeScript? R: Use
useTemplateRef<HTMLInputElement>('usernameInput'). Ouref<HTMLInputElement | null>(null).
📖 Resumo
- As referências de modelo são usadas para acessar diretamente elementos do DOM ou instâncias de componentes filhos
- 3 maneiras de escrever isso: referência de variável (Vue 3) / useTemplateRef (Vue 3.5+; recomendado) / referência de string (estilo Vue 2; não recomendado)
- 5 operações básicas: focar / rolar / selecionar / alterar propriedades / ajustar componentes
defineExposepermite que um componente filho exponha métodos ao seu pai- Em
v-for,refé automaticamente coletado em uma matriz - 5 cenários principais: Foco automático / Rolagem até o final da página / Método de ajuste de tom / Integração com aplicativos de terceiros / Medição do tamanho do elemento
- 4 antipadrões: erros ortográficos / acesso de nível superior / janelas pop-up que não utilizam
nextTick/ entre componentesref
📝 Exercícios
-
Questões básicas (Dificuldade: ⭐)
Implemente um formulário simples de foco automático:
- 1 campo de entrada + 1 botão
- O campo de entrada recebe o foco automaticamente após o carregamento da página
- Quando o botão é clicado, o texto do botão muda para “Enviado” e o botão fica desativado
-
Problemas avançados (Dificuldade: ⭐⭐)
Implementar uma janela de bate-papo que role automaticamente até o final da página:
- Lista de mensagens (contêiner div)
- Campo de entrada + botão “Enviar”
- Depois que uma mensagem é enviada, ela é adicionada à lista e a tela rola automaticamente até o final
- Use
v-forpara renderizar mensagens e uma matrizrefpara gerenciar o DOM
-
Problema desafiador (Dificuldade: ⭐⭐⭐)
Implementar um sistema completo de “componentes pai-filho + Template Refs”:
- Subcomponente FormValidator: disponibiliza os métodos validate() e reset()
- Componente pai da LoginPage: chama o método
validatepara realizar a validação; exibe uma mensagem de erro caso a validação falhe - 5 campos de preenchimento (nome de usuário/e-mail/senha/telefone/captcha)
- useTemplateRef (Vue 3.5+) + tipagem forte do TypeScript
- Colocar o foco automaticamente no primeiro campo de entrada quando a janela pop-up for aberta
- Quando ocorrer um erro, concentre-se no primeiro campo de erro