Skip to content

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

Abychom získali referenci s Composition API, 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
    }
  }
}

Výsledný ref je zpřístupněn jako this.$refs:

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

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

Upozorňujeme, že k ref můžete přistupovat pouze po připojení komponenty. Pokud se pokusíte o přístup k $refs.inputinput ve výrazu šablony, bude při prvním vykreslení undefinednull. 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

Vyžaduje v3.2.25 nebo vyšší

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

Vyzkoušejte si to

Když je ref použitý uvnitř v-for, výsledná ref hodnota bude pole obsahující příslušné 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>

Vyzkoušejte si to

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

export default {
  components: {
    Child
  },
  mounted() {
    // this.$refs.child bude obsahovat instanci <Child />
  }
}
</script>

<template>
  <Child ref="child" />
</template>

Pokud komponenta potomka používá Options API nebo nepoužívá <script setup>, odkazovaná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í propsemit.

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

K omezení přístupu k instanci potomka lze použít volbu expose:

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

Ve výše uvedeném příkladu bude mít rodič odkazující na tuto komponentu prostřednictvím template ref přístup pouze na publicData a publicMethod.

Template refs has loaded