Les refs du template
Bien que le modèle de rendu déclaratif de Vue fasse abstraction pour vous de la plupart des opérations directes sur le DOM, il peut tout de même y avoir des cas où nous avons besoin d'accéder aux éléments sous-jacents du DOM. Pour ce faire, nous pouvons utiliser l'attribut spécial ref
:
template
<input ref="input">
ref
est un attribut spécial, semblable à l'attribut key
évoqué dans le chapitre v-for
. Il nous permet d'obtenir une référence directe à un élément spécifique du DOM ou à une instance d'un composant enfant une fois qu'il a été monté. Cela peut être utile lorsque vous voulez, par exemple, vous concentrer de manière programmatique sur une entrée lors du montage d'un composant, ou bien initialiser une librairie tierce sur un élément.
Accéder aux refs
Pour obtenir la référence avec l'API Composition, nous pouvons utiliser le helper useTemplateRef()
:
vue
<script setup>
import { useTemplateRef, onMounted } from 'vue'
// le premier argument doit correspondre à la valeur ref dans le template
const input = useTemplateRef('my-input')
onMounted(() => {
input.value.focus()
})
</script>
<template>
<input ref="my-input" />
</template>
Lorsque vous utilisez TypeScript, le support IDE de Vue et vue-tsc
déduiront automatiquement le type de input.value
en fonction de l'élément ou du composant sur lequel l'attribut ref
correspondant est utilisé.
Utilisation avant 3.5
Dans les versions antérieures à la 3.5 où useTemplateRef()
n'a pas été introduit, nous devons déclarer une ref avec un nom qui correspond à la valeur de l'attribut template ref :
vue
<script setup>
import { ref, onMounted } from 'vue'
// déclare une ref contenant la référence à l'élément
// le nom doit correspondre à la valeur de la ref dans le template
const input = ref(null)
onMounted(() => {
input.value.focus()
})
</script>
<template>
<input ref="input" />
</template>
Si vous n'utilisez pas <script setup>
, assurez vous également de retourner la ref depuis setup()
:
js
export default {
setup() {
const input = ref(null)
// ...
return {
input
}
}
}
Notez que vous ne pouvez accéder à la ref qu'après que le composant ait été monté. Si vous essayez d'accéder à input
via une expression dans le template, ça sera null
lors du premier rendu. C'est parce que l'élément n'existe pas avant la fin du premier rendu !
Si vous essayez d'observer les changements d'une ref du template, assurez vous de prendre en compte le cas où la ref a une valeur null
:
js
watchEffect(() => {
if (input.value) {
input.value.focus()
} else {
// pas encore monté ou l'élément a été démonté (par ex. par v-if)
}
})
Voir aussi : Typer les refs du template
Refs à l'intérieur d'un v-for
Requiert v3.5 ou ultérieure
Lorsque ref
est utilisée à l'intérieur d'un v-for
, la ref correspondante doit contenir un tableau, qui sera alimenté avec les éléments après le montage :
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>
Utilisation avant 3.5
Dans les versions antérieurs à la 3.5 où useTemplateRef()
n'était pas encore introduit, nous devions déclarer une ref avec un nom qui corresponde à la valeur de l'attribut ref du template. La ref devait également contenir un valeur sous forme de tableau :
In versions before 3.5 where useTemplateRef()
was not introduced, we need to declare a ref with a name that matches the template ref attribute's value. The ref should also contain an array value:
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>
À noter que le tableau ref ne garantit pas le même ordre que celui du tableau source.
Les fonctions refs
Au lieu d'une chaîne de caractères comme clé, l'attribut ref
peut également être lié à une fonction, qui sera appelée à chaque mise à jour du composant et vous donne une flexibilité totale sur l'endroit où stocker la référence à l'élément. La fonction reçoit la référence à l'élément comme premier argument :
template
<input :ref="(el) => { /* assigne l'élément à une propriété ou à une ref */ }">
Notez l'utilisation d'une liaison dynamique :ref
permettant de passer une fonction au lieu d'un nom de ref en chaîne de caractères. Lorsque l'élément est démonté, l'argument sera null
. Vous pouvez, bien entendu, utiliser une méthode au lieu d'une fonction en une ligne.
Ref sur un composant
Cette section considère les composants comme acquis. N'hésitez pas à la passer et revenir plus tard.
ref
peut également être utilisée sur un composant enfant. Dans ce cas la référence sera celle d'une instance du composant :
vue
<script setup>
import { useTemplateRef, onMounted } from 'vue'
import Child from './Child.vue'
const childRef = useTemplateRef('child')
onMounted(() => {
// childRef.value contiendra une instance de <Child />
})
</script>
<template>
<Child ref="child" />
</template>
Utilisation avant 3.5
vue
<script setup>
import { ref, onMounted } from 'vue'
import Child from './Child.vue'
const child = ref(null)
onMounted(() => {
// child.value contiendra une instance de <Child />
})
</script>
<template>
<Child ref="child" />
</template>
Si le composant enfant utilise l'Options API ou n'utilise pas <script setup>
, l' instance référencée sera identique au this
du composant enfant, ce qui signifie que le composant parent aura un accès total aux propriétés et méthodes du composant enfant. Cela simplifie la création de détails de mise en oeuvre étroitement liés entre le parent en l'enfant, donc les refs sur un composant devraient être utilisées seulement si nécessaire - dans la plupart des cas, vous devriez essayez d'implémenter des interactions parent / enfant en utilisant d'abord les props standards et les interfaces emit.
Une exception ici est que les composants utilisant <script setup>
sont privés par défaut : un composant parent faisant référence à un composant enfant en utilisant <script setup>
n'aura accès à rien, à moins que le composant enfant choisisse d'exposer une interface publique via la macro defineExpose
:
vue
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
// Les macros de compilation, telle que defineExpose, n'ont pas besoin d'être importées
defineExpose({
a,
b
})
</script>
Lorsqu'un parent accède à une instance de ce composant via les refs du template, l'instance récupérée aura la forme suivante : { a: number, b: number }
(les refs sont automatiquement désenveloppées comme sur les instances classiques).
Notez que defineExpose doit être appelé avant toute opération await. Sinon, les propriétés et les méthodes exposées après l'opération await ne seront pas accessibles.
Voir aussi : Typer les refs du template d'un composant