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čů:

prop drilling diagram

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ů, bezohledu na hloubku jejího zanoření, může implementovat (inject) závislosti poskytované komponentami v rodčovském řetězci.

Provide/inject scheme

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>, ujistetě 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 komponentně 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
    }
  }
}

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

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 žáná 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ůžte 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 rektivity and Computed proměnné s preferencí nastavenou na Composition API.

Práce s klíči typu Symbol

Dosud jsme v příkladech používali injection kyes 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 kyes typu Symbol, abyste se vyhnuli případným kolizím.

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

js
// keys.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