Template Refs
Aunque el modelo de renderizado declarativo de Vue abstrae la mayoría de las operaciones directas del DOM por ti, todavía puede haber casos en los que necesitemos acceso directo a los elementos del DOM subyacentes. Para lograr esto, podemos usar el atributo especial ref:
template
<input ref="input">ref es un atributo especial, similar al atributo key discutido en el capítulo de v-for. Nos permite obtener una referencia directa a un elemento DOM específico o a una instancia de componente hijo después de que se monta. Esto puede ser útil cuando quieres, por ejemplo, enfocar programáticamente un input al montar un componente, o inicializar una librería de terceros en un elemento.
Accediendo a las Refs
Para obtener la referencia con la Composition API, podemos usar el helper useTemplateRef() :
vue
<script setup>
import { useTemplateRef, onMounted } from 'vue'
// the first argument must match the ref value in the template
const input = useTemplateRef('my-input')
onMounted(() => {
input.value.focus()
})
</script>
<template>
<input ref="my-input" />
</template>Cuando se utiliza TypeScript, el soporte IDE de Vue y vue-tsc inferirán automáticamente el tipo de input.value basándose en el elemento o componente en el que se utiliza el atributo ref coincidente.
Uso antes de 3.5
En versiones anteriores a la 3.5, donde useTemplateRef() no había sido introducido, necesitamos declarar una ref con un nombre que coincida con el valor del atributo ref del template:
vue
<script setup>
import { ref, onMounted } from 'vue'
// declare a ref to hold the element reference
// the name must match template ref value
const input = ref(null)
onMounted(() => {
input.value.focus()
})
</script>
<template>
<input ref="input" />
</template>Si no estás usando <script setup>, asegúrate de también retornar la ref desde setup():
js
export default {
setup() {
const input = ref(null)
// ...
return {
input
}
}
}Ten en cuenta que solo puedes acceder a la ref después de que el componente esté montado. Si intentas acceder a input en una expresión de template, será null en el primer renderizado. ¡Esto se debe a que el elemento no existe hasta después del primer renderizado!
Si estás intentando observar los cambios de una ref de template, asegúrate de considerar el caso en que la ref tenga un valor null:
js
watchEffect(() => {
if (input.value) {
input.value.focus()
} else {
// no se ha montado todavía, o el elemento se ha desmontado (por ejemplo, mediante v-if)
}
})Ver también: Tipado de Template Refs
Ref en Componente
Esta sección asume conocimientos de Componentes. Siéntete libre de saltarla y volver más tarde.
ref también puede ser usado en un componente hijo. En este caso, la referencia será la de una instancia de componente:
vue
<script setup>
import { useTemplateRef, onMounted } from 'vue'
import Child from './Child.vue'
const childRef = useTemplateRef('child')
onMounted(() => {
// childRef.value contendrá una instancia de <Child />.
})
</script>
<template>
<Child ref="child" />
</template>Uso antes de 3.5
vue
<script setup>
import { ref, onMounted } from 'vue'
import Child from './Child.vue'
const child = ref(null)
onMounted(() => {
// child.value contendrá una instancia de <Child />.
})
</script>
<template>
<Child ref="child" />
</template>Si el componente hijo está usando la Options API o no está usando <script setup>, la instancia referenciada será idéntica al this del componente hijo, lo que significa que el componente padre tendrá acceso completo a cada propiedad y método del componente hijo. Esto facilita la creación de detalles de implementación fuertemente acoplados entre el padre y el hijo, por lo que las ref de componentes solo deben usarse cuando sea absolutamente necesario; en la mayoría de los casos, primero deberías intentar implementar las interacciones padre/hijo utilizando las interfaces estándar de props y emit.
Una excepción aquí es que los componentes que usan <script setup> son privados por defecto: un componente padre que referencia un componente hijo usando <script setup> no podrá acceder a nada a menos que el componente hijo elija exponer una interfaz pública usando la macro defineExpose:
vue
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
// Las macros del compilador, como defineExpose, no necesitan importarse
defineExpose({
a,
b
})
</script>Cuando un padre obtiene una instancia de este componente a través de ref de template, la instancia recuperada tendrá la forma { a: number, b: number } (las ref se desenvuelven automáticamente al igual que en las instancias normales).
Ten en cuenta que defineExpose debe llamarse antes de cualquier operación await. De lo contrario, las propiedades y métodos expuestos después de la operación await no serán accesibles.
Ver también: Tipado de Template Refs de Componente
Refs dentro de v-for
Requiere v3.5 o superior
Cuando se usa ref dentro de v-for, la ref correspondiente debería contener un valor Array, que se poblará con los elementos después del montaje:
vue
<script setup>
import { ref, useTemplateRef, onMounted } from 'vue'
const list = ref([
/* ... */
])
const itemRefs = useTemplateRef('items')
onMounted(() => console.log(itemRefs.value))
</script>
<template>
<ul>
<li v-for="item in list" ref="items">
{{ item }}
</li>
</ul>
</template>Uso antes de 3.5
En versiones anteriores a la 3.5, donde useTemplateRef() no había sido introducido, necesitamos declarar una ref con un nombre que coincida con el valor del atributo ref del template. La ref también debería contener un valor de array:
vue
<script setup>
import { ref, onMounted } from 'vue'
const list = ref([
/* ... */
])
const itemRefs = ref([])
onMounted(() => console.log(itemRefs.value))
</script>
<template>
<ul>
<li v-for="item in list" ref="itemRefs">
{{ item }}
</li>
</ul>
</template>Cabe señalar que el array de ref no garantiza el mismo orden que el array fuente.
Refs de Funciones
En lugar de una clave de cadena, el atributo ref también puede vincularse a una función, que se llamará en cada actualización del componente y te da total flexibilidad sobre dónde almacenar la referencia del elemento. La función recibe la referencia del elemento como primer argumento:
template
<input :ref="(el) => { /* asignar el a una propiedad o ref */ }">Ten en cuenta que estamos usando un enlace dinámico :ref para poder pasarle una función en lugar de una cadena de nombre de ref. Cuando el elemento se desmonta, el argumento será null. Por supuesto, puedes usar un método en lugar de una función inline.