Template refs
I když od vás deklarativní model vykreslování ve Vue většinu přímých operací s DOM abstrahuje, stále mohou nastat případy, kdy potřebujeme k základním elementům DOM přímý přístup. Abychom toho dosáhli, můžeme použít speciální atribut ref
:
template
<input ref="input">
ref
je speciální atribut, podobný jako atribut key
popsaný v kapitole v-for
. Umožňuje nám získat přímý odkaz na konkrétní DOM element nebo instanci podřízené komponenty poté, co je připojena (mounted). To může být užitečné, když chcete například programově zaměřit vstup na připojenou komponentu nebo na elementu inicializovat knihovnu třetí strany.
Přístup k refs
Pro získání odkazu s Composition API můžeme využít pomocnou funkci useTemplateRef()
:
vue
<script setup>
import { useTemplateRef, onMounted } from 'vue'
// první parametr musí odpovídat hodnotě ref v šabloně
const input = useTemplateRef('muj-ref')
onMounted(() => {
input.value.focus()
})
</script>
<template>
<input ref="muj-ref" />
</template>
Při použití TypeScriptu podpora Vue v IDE a vue-tsc
automaticky odvodí typ input.value
podle toho, na jaký element nebo komponentu je použit odpovídající ref
atribut.
Použití před verzí 3.5
Ve verzích před 3.5, kde useTemplateRef()
ještě není dostupné, musíme deklarovat ref s názvem, který odpovídá hodnotě atributu ref
v šabloně:
vue
<script setup>
import { ref, onMounted } from 'vue'
// deklarujte ref, který bude obsahovat referenci na element
// jméno musí odpovídat hodnotě template ref
const input = ref(null)
onMounted(() => {
input.value.focus()
})
</script>
<template>
<input ref="input" />
</template>
Pokud nepoužíváte <script setup>
, ujistěte se, že ref v metodě setup()
také vracíte:
js
export default {
setup() {
const input = ref(null)
// ...
return {
input
}
}
}
Upozorňujeme, že k ref můžete přistupovat pouze po připojení komponenty. Pokud se pokusíte o přístup k input
ve výrazu šablony, bude při prvním vykreslení null
. Je to proto, že element existuje až po prvním vykreslení!
Pokud se pokoušíte na template ref apikovat watcher, nezapomeňte vzít v úvahu případ, kdy má ref hodnotu null
:
js
watchEffect(() => {
if (input.value) {
input.value.focus()
} else {
// ještě není připojeno, nebo je komponenta ve stavu "unmounted"
// (například kvůli v-if)
}
})
Viz také: Typování Template Refs
Refs uvnitř v-for
Podporováno až od verze 3.5+
Když je ref
použitý uvnitř v-for
, odpovídající ref by měl obsahovat prázdné pole, které bude po připojení komponenty naplněno příslušnými elementy:
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>
Použití před verzí 3.5
Ve verzích před 3.5, kde useTemplateRef()
ještě není dostupné, musíme deklarovat ref s názvem, který odpovídá hodnotě atributu ref
v šabloně. Ref by měl obsahovat hodnotu typu pole:
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>
Je třeba poznamenat, že ref pole nezaručuje stejné pořadí jako zdrojové pole.
Funkční refs
Namísto klíče typu string může být atribut ref
svázán i s funkcí, která bude volána při každé aktualizaci komponenty a poskytne vám plnou flexibilitu, kam uložit odkaz na element. Funkce obdrží odkaz na element jako první parametr:
template
<input :ref="(el) => { /* přiřadit `el` do proměnné nebo ref */ }">
Všimněte si, že používáme dynamický binding :ref
, takže můžeme předat přímo funkci místo názvu ref v podobě string. Když je prvek odpojen, parametr bude null
. Místo inline funkce můžete samozřejmě použít metodu.
Ref na komponentě
Tato sekce předpokládá znalost základů komponent. Klidně ji teď přeskočte a vraťte se později.
ref
může být také použit na komponentu potomka. V tomto případě povede reference na odpovídající instanci komponenty:
vue
<script setup>
import { useTemplateRef, onMounted } from 'vue'
import Child from './Child.vue'
const childRef = useTemplateRef('child')
onMounted(() => {
// childRef.value bude obsahovat instanci <Child />
})
</script>
<template>
<Child ref="child" />
</template>
Použití před verzí 3.5
vue
<script setup>
import { ref, onMounted } from 'vue'
import Child from './Child.vue'
const child = ref(null)
onMounted(() => {
// child.value bude obsahovat instanci <Child />
})
</script>
<template>
<Child ref="child" />
</template>
Pokud komponenta potomka používá Options API nebo nepoužívá <script setup>
, odkazovaná instance bude identická s this
komponenty potomka, což znamená, že komponenta rodiče bude mít plný přístup ke každé vlastnosti a metodě komponenty potomka. To usnadňuje vytváření těsně propojených implementačních detailů mezi rodičem a potomkem, takže odkazy na komponenty by se měly používat pouze tehdy, když je to absolutně nutné – ve většině případů byste se měli nejprve pokusit implementovat interakce rodiče a potomka pomocí standardních rozhraní props
a emit
.
Výjimkou zde je, že komponenty používající <script setup>
jsou ve výchozím nastavení soukromé: komponenta rodiče odkazující na komponentu potomka pomocí <script setup>
nebude mít přístupk ničemu, pokud se komponenta potomka nerozhodne vystavit své veřejné rozhraní pomocí makra defineExpose
:
vue
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
// makra překladače, jako je defineExpose, nemusí být importovány
defineExpose({
a,
b
})
</script>
Když rodič získá instanci této komponenty prostřednictvím template refs, získaná instance bude mít tvar { a: number, b: number}
(refs se automaticky rozbalí stejně jako u normálních instancí).
Viz také: Typování Template refs komponenty