Skip to content

Provide / Inject ​

Tato stránka předpokládá, že už jste četli Základy komponent. Pokud jsou pro vás komponenty nové, přečtěte si je jako první.

Drilling vlastností ​

Pokud potřebujeme předat data z nadřazené komponenty podřazené komponentě, použijeme obvykle vlastnosti (props). Představte si však případ, kdy máme rozsáhlý strom komponent a hluboko vnořená komponenta potřebuje něco ze komponenty vzdáleného předka. Pokud bychom používali pouze vlastnosti, museli bychom předávat stejnou vlastnost napříč celým řetězcem rodičů:

Diagram ukazuje předávání vlastností (props) přes více úrovní komponent až do hluboce zanořené komponenty potomka

Všimněte si, že ačkoli komponentu <Footer> tyto vlastnosti možná vůbec nezajímají, musí je deklarovat a předat dál, aby k nim komponenta <DeepChild> měla přístup. Pokud by existoval delší rodičovský řetězec, ovlivnilo by to po cestě ještě více komponent. Tomu se říká „props drilling“ a rozhodně není zábavné se s tím potýkat.

Drilling vlastností můžeme řešit pomocí provide a inject. Komponenta rodiče může sloužit jako poskytovatel závislostí (dependency provider) pro všechny své potomky. Jakákoli komponenta ve stromu potomků, bez ohledu na hloubku jejího zanoření, může implementovat (inject) závislosti poskytované komponentami v rodičovském řetězci.

Diagram ukazuje mechanismus provide/inject, kde komponenta rodiče poskytuje závislost, kterou může hluboce zanořená komponenta potomka implementovat přímo a obejít mezilehlé komponenty

Provide ​

Pro poskytnutí dat komponentám potomků použijte funkci provide():

vue
<script setup>
import { provide } from 'vue'

provide(/* klíč */ 'message', /* hodnota */ 'Ahoj!')
</script>

Pokud nepoužíváte <script setup>, ujistěte se, že je provide() voláno synchronně uvnitř setup() funkce:

js
import { provide } from 'vue'

export default {
  setup() {
    provide(/* klíč */ 'message', /* hodnota */ 'Ahoj!')
  }
}

Funkce provide() přijímá dva parametry. První parametr se nazývá injection key, což může být string nebo Symbol. Injection key je použit v komponentě potomka k vyhledání hodnoty, která má být implementována. Jedna komponenta může volat provide() vícekrát s různými injection keys pro poskytnutí různých hodnot.

Druhý parametr je poskytovaná hodnota. Hodnota může být jakéhokoli typu vč. reaktivního stavu jako jsou refs:

js
import { ref, provide } from 'vue'

const count = ref(0)
provide('key', count)

Poskytnutí reaktivních hodnot umožňuje komponentám potomků, které poskytnutou hodnotu používají, navázat reaktivní spojení s komponentou zprostředkovatele.

Pro poskytnutí dat komponentám potomků použijte možnost provide:

js
export default {
  provide: {
    message: 'Ahoj!'
  }
}

Pro každou proměnnou objektu provide, je jeho klíč použit v komponentě potomka k lokalizaci správné hodnoty (injection key), zatímco hodnota bude implementována.

Pokud potřebujeme poskytovat stav konkrétní instance, například data deklarovaná pomocí data(), pak je třeba použít provide ve tvaru funkce:

js
export default {
  data() {
    return {
      message: 'Ahoj!'
    }
  },
  provide() {
    // použijeme syntaxi funkce, abychom mohli přistoupit k `this`
    return {
      message: this.message
    }
  }
}

Vezměte ovšem na vědomí, že toto nezajistí reaktivitu implementace. Jak udělat reaktivní implementaci budeme probírat později.

Provide na úrovni aplikace ​

Kromě poskytování dat v komponentě můžeme data poskytovat také na úrovni celé aplikace:

js
import { createApp } from 'vue'

const app = createApp({})

app.provide(/* klíč */ 'message', /* hodnota */ 'Ahoj!')

Provide na úrovni aplikace je k dispozici všem komponentám vykresleným v aplikaci. Zvlášť užitečné je to při psaní pluginů, protože pluginy obvykle nejsou schopny poskytovat hodnoty pomocí komponent.

Inject ​

Pro implementaci dat poskytnutých komponentou předka použijte funkci inject():

vue
<script setup>
import { inject } from 'vue'

const message = inject('message')
</script>

Pokud více komponent v hierarchii rodičů poskytuje hodnoty se stejným klíčem, inject bude vyhodnoceno na hodnotu z komponenty nejblíže té cílové.

Pokud je poskytovaná hodnota ref, bude jako ref implementována a nebude automaticky rozbalena. To umožňuje komponentě, která implementuje, zachovat reaktivitu spojení s komponentou, která data poskytuje.

Kompletní provide/inject příklad vč. reaktivity

Opět, pokud nepoužíváte <script setup>, inject() lze volat pouze synchronně uvnitř setup():

js
import { inject } from 'vue'

export default {
  setup() {
    const message = inject('message')
    return { message }
  }
}

Pro implementaci dat poskytnutých komponentou předka použijte možnost inject:

js
export default {
  inject: ['message'],
  created() {
    console.log(this.message) // implementovaná hodnota
  }
}

Implementace jsou vyhodnoceny dříve než vlastní stav komponenty, takže lze na implementované hodnoty přistupovat v možnosti data():

js
export default {
  inject: ['message'],
  data() {
    return {
      // výchozí data založená na implementované hodnotě
      fullMessage: this.message
    }
  }
}

Pokud dvě rodičovské komponenty poskytují data se stejným klíčem, implementace použije hodnotu z nejbližší komponenty v rodičovském řetězci.

Kompletní provide/inject příklad

Injection aliasing ​

Pokud je pro inject použita syntaxe pole, jsou implementované vlastnosti vystaveny na instanci komponenty pomocí stejného klíče. Ve výše uvedeném příkladu byla vlastnost poskytnuta pod klíčem "message" a implementováína jako this.message. Lokální klíč je stejný jako injection key.

Pokud chceme implementovat vlastnost pomocí jiného lokálního klíče, musíme pro možnost inject použít objektovou syntaxi:

js
export default {
  inject: {
    /* lokální klíč */ localMessage: {
      from: /* injection key */ 'message'
    }
  }
}

V tomto případě nalezne komponenta vlastnost poskytovanou pod klíčem "message" a vystaví ji jako this.localMessage.

Výchozí hodnoty pro implementaci ​

Ve výchozím nastavení inject předpokládá, že implementovaná hodnota je někde v rodičovském řetězci poskytována. V případě, že klíč poskytnut není, zobrazí se runtime varování.

Pokud chceme, aby implementovaná vlastnost fungovala s volitelnými poskytovateli, musíme deklarovat výchozí hodnotu, podobně jako u vlastností:

js
// pokud není poskytnuta žádná odpovídající "message"
// `value` bude "default value"
const value = inject('message', 'default value')

V některých případech může být nutné výchozí hodnotu vytvořit voláním funkce nebo instancí nové třídy. Abychom se vyhnuli zbytečným výpočtům nebo vedlejším efektům v případě, že volitelnou hodnotu nepoužijeme, můžeme pro vytvoření výchozí hodnoty použít tovární (factory) metodu:

js
const value = inject('key', () => new ExpensiveClass(), true)

Třetí parametr indikuje, že výchozí hodnota by měla být považována za tovární funkci.

js
export default {
  // pro deklaraci výchozích hodnot pro implementaci
  // je nutná objektová syntaxe
  inject: {
    message: {
      from: 'message', // pokud chceme použít stejný klíč, je toto nepovinné
      default: 'default value'
    },
    user: {
      // pro složitější hodnoty, které jsou drahé na tvorbu
      // nebo mají být unikátní pro každou instanci komponenty,
      // použijeme tovární metodu
      default: () => ({ name: 'John' })
    }
  }
}

Práce s reaktivitou ​

Když používáme reaktivní provide/inject hodnoty, je doporučeno provádět všechny změny reaktivního stavu uvnitř provider komponenty, kdykoli je to možné. Tím je zajištěno, že jsou poskytnutý stav a jeho případné změny umístěny ve stejné komponentě, což usnadňuje budoucí údržbu.

V některých případech můžeme potřebovat aktualizovat data z komponenty, která poskytnutá data implementuje. V takových případech doporučujeme poskytovat funkci, která je za změny stavu zodpovědná:

vue
<!-- uvnitř komponenty, která poskytuje -->
<script setup>
import { provide, ref } from 'vue'

const location = ref('Severní pól')

function updateLocation() {
  location.value = 'Jižní pól'
}

provide('location', {
  location,
  updateLocation
})
</script>
vue
<!-- uvnitř komponenty, která implementuje -->
<script setup>
import { inject } from 'vue'

const { location, updateLocation } = inject('location')
</script>

<template>
  <button @click="updateLocation">{{ location }}</button>
</template>

Pokud se chcete ujistit, že data předaná skrz provide nemohou být změněna komponentou, která je implementuje, můžete poskytovanou hodnotu obalit pomocí readonly().

vue
<script setup>
import { ref, provide, readonly } from 'vue'

const count = ref(0)
provide('read-only-count', readonly(count))
</script>

Aby byla implementace reaktivně propojena se komponentou poskytovatele, musíme poskytovat computed proměnnou pomocí funkce computed():

js
import { computed } from 'vue'

export default {
  data() {
    return {
      message: 'Ahoj!'
    }
  },
  provide() {
    return {
      // explicitně poskytujeme computed proměnnou
      message: computed(() => this.message)
    }
  }
}

Kompletní provide/inject příklad vč. reaktivity

Funkce computed() se typicky používá v komponentách psaných v Composition API, ale lze ji také použít pro doplnění některých případů užití v Options API. O jejím použití se můžete dočíst víc v průvodcích Základy reaktivity a Computed proměnné s preferencí nastavenou na Composition API.

Práce s klíči typu Symbol ​

Dosud jsme v příkladech používali injection keys typu string. Pokud pracujete v rozsáhlé aplikaci s mnoha poskytovateli závislostí nebo vytváříte komponenty, které budou používat i další vývojáři, je nejlepší používat injection keys typu Symbol, abyste se vyhnuli případným kolizím.

Je doporučeno exportovat použité symboly do vyhrazeného souboru:

keys.js
js
export const myInjectionKey = Symbol()
js
// uvnitř komponenty, která poskytuje
import { provide } from 'vue'
import { myInjectionKey } from './keys.js'

provide(myInjectionKey, {
  /* poskytovaná data */
})
js
// uvnitř komponenty, která implementuje
import { inject } from 'vue'
import { myInjectionKey } from './keys.js'

const injected = inject(myInjectionKey)

Viz také: Typování Provide / Inject

js
// uvnitř komponenty, která poskytuje
import { myInjectionKey } from './keys.js'

export default {
  provide() {
    return {
      [myInjectionKey]: {
        /* poskytovaná data */
      }
    }
  }
}
js
// uvnitř komponenty, která implementuje
import { myInjectionKey } from './keys.js'

export default {
  inject: {
    injected: { from: myInjectionKey }
  }
}
Provide / Inject has loaded