Referencje w szablonach
Chociaż deklaratywny model renderowania Vue abstrahuje większość operacji na DOM, mogą zdarzyć się przypadki, w których potrzebujemy bezpośredniego dostępu do elementów DOM. Aby to osiągnąć, możemy użyć specjalnego atrybutu ref
:
template
<input ref="input">
ref
to specjalny atrybut, podobny do atrybutu key
omawianego w rozdziale v-for
. Pozwala nam uzyskać bezpośrednie referencję do konkretnego elementu DOM lub instancji komponentu dziecka po jej zamontowaniu. Może to być przydatne, gdy chcemy na przykład programowo ustawić fokus na inpucie podczas montowania komponentu lub zainicjować bibliotekę zewnętrzną na elemencie.
Dostęp do referencji
Aby uzyskać referencję do elementu przy użyciu Composition API, możemy użyć helpera useTemplateRef()
:
vue
<script setup>
import { useTemplateRef, onMounted } from 'vue'
// pierwszy argument musi odpowiadać atrybutowi ref w szablonie
const input = useTemplateRef('my-input')
onMounted(() => {
input.value.focus()
})
</script>
<template>
<input ref="my-input" />
</template>
Używając TypeScripta z Vue, IDE razem z vue-tsc
powinny automatycznie wywnioskować typ input.value
na podstawie elementu lub komponentu na podstawie odpowiadającego atrybutu ref
.
Użycie przed 3.5
W wersjach przed 3.5 gdzie useTemplateRef
nie był dostępny, musimy zadeklarować ref z nazwą, która odpowiada atrybutowi ref w szablonie:
vue
<script setup>
import { ref, onMounted } from 'vue'
// zadeklaruj ref do przechowywania referencji do elementu
// nazwa musi pasować do wartości atrybutu ref w szablonie
const input = ref(null)
onMounted(() => {
input.value.focus()
})
</script>
<template>
<input ref="input" />
</template>
Jeśli nie używasz <script setup>
, upewnij się, że zwrócisz ref z funkcji setup()
:
js
export default {
setup() {
const input = ref(null)
// ...
return {
input
}
}
}
Należy pamiętać, że dostęp do referencji jest możliwy dopiero po zamontowaniu komponentu. Jeśli spróbujesz uzyskać dostęp do input
w wyrażeniu szablonu, będzie to null
przy pierwszym renderowaniu. Dzieje się tak, ponieważ element nie istnieje aż do pierwszego renderowania!
Jeśli próbujesz obserwować zmiany referencji w szablonie, upewnij się, że uwzględniasz przypadek, gdy referencja ma wartość null
:
js
watchEffect(() => {
if (input.value) {
input.value.focus()
} else {
// nie jest jeszcze zamontowany, lub element został odmontowany (np. przez v-if)
}
})
Zobacz także: Typowanie referencji w szablonie
Referencje wewnątrz v-for
Wymaga wersji v3.5 lub wyższej
Gdy ref
jest używane wewnątrz v-for
, odpowiednia referencja powinna zawierać wartość tablicy, która zostanie wypełniona elementami po zamontowaniu:
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>
Użycie przed 3.5
W wersjach przed 3.5, gdzie useTemplateRef()
nie było dostępne, musimy zadeklarować ref z nazwą która odpowiada atrybutowi ref w szablonie. Ten ref będzie zawierał wartość tablicową:
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>
Należy zauważyć, że tablica referencji nie gwarantuje tej samej kolejności, co tablica źródłowa.
Referencje funkcyjne
Zamiast nazwy klucza, atrybut ref
może być również powiązany z funkcją, która będzie wywoływana przy każdej aktualizacji komponentu i daje pełną elastyczność w przechowywaniu referencji do elementu. Funkcja ta otrzymuje referencję do elementu jako pierwszy argument:
template
<input :ref="(el) => { /* przypisz el do właściwości lub referencji */ }">
Zauważ, że używamy dynamicznego powiązania :ref
, aby przekazać funkcję zamiast ciągu znaków jako nazwę referencji. Gdy element zostanie odmontowany, argument będzie null
. Oczywiście można użyć metody zamiast funkcji inline.
Referencje na komponencie
Ta sekcja zakłada znajomość komponentów. Jeśli chcesz, możesz ją pominąć i wrócić później.
ref
może być również używane na komponencie dziecka. W takim przypadku referencja będzie dotyczyła instancji komponentu:
vue
<script setup>
import { useTemplateRef, onMounted } from 'vue'
import Child from './Child.vue'
const childRef = useTemplateRef('child')
onMounted(() => {
// childRef.value will hold an instance of <Child />
})
</script>
<template>
<Child ref="child" />
</template>
Użycie przed 3.5
vue
<script setup>
import { ref, onMounted } from 'vue'
import Child from './Child.vue'
const child = ref(null)
onMounted(() => {
// child.value będzie przechowywać instancję <Child />
})
</script>
<template>
<Child ref="child" />
</template>
Jeśli komponent dziecka używa Options API lub nie korzysta ze <script setup>
, referowana instancja będzie identyczna z this
komponentu dziecka, co oznacza, że komponent nadrzędny będzie miał pełny dostęp do wszystkich właściwości i metod komponentu dziecka. Ułatwia to tworzenie ściśle powiązanych szczegółów implementacyjnych między rodzicem a dzieckiem, więc referencje do komponentów należy stosować tylko wtedy, gdy jest to absolutnie konieczne – w większości przypadków należy spróbować zaimplementować interakcje rodzic-dziecko przy użyciu standardowych interfejsów props i emit.
Wyjątkiem są komponenty używające <script setup>
, które są domyślnie prywatne: komponent nadrzędny, który odwołuje się do komponentu dziecka używającego <script setup>
, nie będzie miał dostępu do niczego, chyba że komponent dziecka zdecyduje się ujawnić publiczny interfejs za pomocą makro defineExpose
:
vue
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
// Makra kompilatora, takie jak defineExpose, nie muszą być importowane
defineExpose({
a,
b
})
</script>
Kiedy komponent nadrzędny uzyska instancję tego komponentu przez referencje do szablonu, zwrócona instancja będzie miała postać { a: number, b: number }
(referencje są automatycznie rozpakowywane tak jak na zwykłych instancjach).
Należy pamiętać, że defineExpose musi być wywołane przed jakąkolwiek operacją await. W przeciwnym razie właściwości i metody ujawnione po operacji await nie będą dostępne.
Zobacz także: Typowanie referencji w szablonach komponentów