Przejdź bezpośrednio do treści

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
    }
  }
}

Wynikowa referencja jest dostępne przez this.$refs:

vue
<script>
export default {
  mounted() {
    this.$refs.input.focus()
  }
}
</script>

<template>
  <input ref="input" />
</template>

Należy pamiętać, że dostęp do referencji jest możliwy dopiero po zamontowaniu komponentu. Jeśli spróbujesz uzyskać dostęp do $refs.inputinput w wyrażeniu szablonu, będzie to undefinednull 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>

Wypróbuj w playground

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>

Gdy ref jest używane wewnątrz v-for, wynikowa wartość referencji będzie tablicą zawierającą odpowiednie elementy:

vue
<script>
export default {
  data() {
    return {
      list: [
        /* ... */
      ]
    }
  },
  mounted() {
    console.log(this.$refs.items)
  }
}
</script>

<template>
  <ul>
    <li v-for="item in list" ref="items">
      {{ item }}
    </li>
  </ul>
</template>

Wypróbuj w playground

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>
vue
<script>
import Child from './Child.vue'

export default {
  components: {
    Child
  },
  mounted() {
    // this.$refs.child 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>,Wtedy 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

Opcja expose może być użyta, aby ograniczyć dostęp do instancji komponentu:

js
export default {
  expose: ['publicData', 'publicMethod'],
  data() {
    return {
      publicData: 'foo',
      privateData: 'bar'
    }
  },
  methods: {
    publicMethod() {
      /* ... */
    },
    privateMethod() {
      /* ... */
    }
  }
}

W powyższym przykładzie, komponent nadrzędny, który odwołuje się do tego komponentu za pomocą referencji w szablonie, będzie miał dostęp tylko do publicData i publicMethod.

Referencje w szablonachJest załadowany