Przejdź bezpośrednio do treści

Provide / Inject (Dostarczanie / Wstrzykiwanie) ​

Ta strona zakłada, że przeczytałeś już Podstawy Komponentów. Przeczytaj je najpierw, jeśli dopiero zaczynasz pracę z komponentami.

Przekazywanie właściwości (Prop Drilling) ​

Zazwyczaj, gdy potrzebujemy przekazać dane z komponentu nadrzędnego do potomnego, używamy właściwości (props). Jednakże, wyobraź sobie przypadek, w którym mamy rozbudowane drzewo komponentów, a głęboko zagnieżdżony komponent potrzebuje czegoś od bardzo odległego komponentu przodka. Używając tylko właściwości (props), musielibyśmy przekazywać tę samą właściwość przez cały łańcuch komponentów nadrzędnych:

schemat przekazywania właściwości

Zauważ, że chociaż komponent <Footer> może w ogóle nie potrzebować tych właściwości, to nadal musi je zadeklarować i przekazać dalej, aby <DeepChild> mógł uzyskać do nich dostęp. Jeśli łańcuch komponentów nadrzędnych jest dłuższy, więcej komponentów zostałoby dotkniętych po drodze. Jest to nazywane "props drilling" (przekazywaniem właściwości) i zdecydowanie nie jest przyjemne w obsłudze.

Możemy rozwiązać problem przekazywania właściwości za pomocą provide i inject. Komponent nadrzędny może służyć jako dostawca zależności dla wszystkich swoich potomków. Każdy komponent w drzewie potomnym, niezależnie od tego, jak głęboko się znajduje, może wstrzykiwać (inject) zależności dostarczone przez komponenty w jego łańcuchu nadrzędnym.

schemat Provide/inject

Provide (Dostarczanie) ​

Aby dostarczyć dane do komponentów potomnych, użyj funkcji provide():

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

provide(/* klucz */ 'message', /* wartość */ 'hello!')
</script>

Jeśli nie używasz <script setup>, upewnij się, że provide() jest wywoływane synchronicznie wewnątrz setup():

js
import { provide } from 'vue'

export default {
  setup() {
    provide(/* klucz */ 'message', /* wartość */ 'hello!')
  }
}

Funkcja provide() przyjmuje dwa argumenty. Pierwszy argument nazywany jest kluczem wstrzykiwania i może być ciągiem znaków (string) lub Symbol. Klucz wstrzykiwania jest używany przez komponenty potomne do wyszukiwania żądanej wartości do wstrzyknięcia. Pojedynczy komponent może wielokrotnie wywoływać provide() z różnymi kluczami wstrzykiwania, aby dostarczyć różne wartości.

Drugim argumentem jest dostarczana wartość. Wartość może być dowolnego typu, włączając w to stan reaktywny, taki jak refs:

js
import { ref, provide } from 'vue'

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

Dostarczanie wartości reaktywnych pozwala komponentom potomnym, które używają dostarczonej wartości, na ustanowienie reaktywnego połączenia z komponentem dostarczającym.

Aby dostarczyć dane do komponentów potomnych, użyj opcji provide:

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

Dla każdej właściwości w obiekcie provide, klucz jest używany przez komponenty potomne do zlokalizowania odpowiedniej wartości do wstrzyknięcia, podczas gdy wartość jest tym, co zostanie ostatecznie wstrzyknięte.

Jeśli potrzebujemy dostarczyć stan per-instancję, na przykład dane zadeklarowane przez data(), wtedy provide musi użyć wartości funkcji:

js
export default {
  data() {
    return {
      message: 'hello!'
    }
  },
  provide() {
    // użyj składni funkcji, aby uzyskać dostęp do `this`
    return {
      message: this.message
    }
  }
}

Należy jednak zauważyć, że nie sprawia to, że wstrzyknięcie staje się reaktywne. Omówimy jak sprawić, by wstrzyknięcia były reaktywne poniżej.

Dostarczanie na poziomie aplikacji (App-level Provide) ​

Oprócz dostarczania danych w komponencie możemy także dostarczać je na poziomie aplikacji:

js
import { createApp } from 'vue'

const app = createApp({})

app.provide(/* klucz */ 'message', /* wartość */ 'hello!')

Wartości dostarczone na poziomie aplikacji są dostępne dla wszystkich komponentów renderowanych w aplikacji. Jest to szczególnie przydatne podczas pisania wtyczek, ponieważ wtyczki zazwyczaj nie mogłyby dostarczać wartości za pomocą komponentów.

Wstrzykiwanie (Inject) ​

Aby wstrzyknąć dane dostarczone przez komponent nadrzędny, użyj funkcji inject():

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

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

Jeśli wielu rodziców dostarcza wartości pod tym samym kluczem, inject wybierze wartość od najbliższego rodzica w łańcuchu rodziców komponentu.

Jeśli dostarczona wartość jest referencją (ref), zostanie wstrzyknięta bez zmian i nie zostanie automatycznie rozpakowana. Pozwala to komponentowi wstrzykującemu zachować połączenie reaktywności z komponentem dostarczającym.

Pełny przykład provide + inject z reaktywnością

Ponownie, jeśli nie używasz <script setup>, inject() powinno być wywoływane tylko synchronicznie wewnątrz setup():

js
import { inject } from 'vue'

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

By wstrzyknąć dane dostarczone przez nadrzędny komponent, użyj opcji inject:

js
export default {
  inject: ['message'],
  created() {
    console.log(this.message) // wstrzyknięta wartość
  }
}

Wstrzyknięcia są rozwiązywane przed utworzeniem stanu komponentu, więc możesz uzyskać dostęp do wstrzykniętych właściwości w data():

js
export default {
  inject: ['message'],
  data() {
    return {
      // initial data based on injected value
      fullMessage: this.message
    }
  }
}

Jeśli wielu rodziców dostarcza wartości pod tym samym kluczem, inject wybierze wartość od najbliższego rodzica w łańcuchu rodziców komponentu.

Pełny przykład provide + inject

Aliasy wstrzyknięć ​

Podczas używania składni tablicowej dla inject, wstrzyknięte właściwości są udostępniane w instancji komponentu przy użyciu tego samego klucza. W powyższym przykładzie właściwość została dostarczona pod kluczem "message" i wstrzyknięta jako this.message. Lokalny klucz jest taki sam jak klucz wstrzyknięcia.

Jeśli chcemy wstrzyknąć właściwość używając innego klucza lokalnego, musimy użyć składni obiektowej dla opcji inject:

js
export default {
  inject: {
    /* lokalny klucz */ localMessage: {
      from: /* klucz wstrzykiwany */ 'message'
    }
  }
}

W tym przypadku komponent zlokalizuje właściwość dostarczoną z kluczem "message", a następnie udostępni ją jako this.localMessage.

Domyślne wartości wstrzyknięć ​

By default, inject assumes that the injected key is provided somewhere in the parent chain. In the case where the key is not provided, there will be a runtime warning.

If we want to make an injected property work with optional providers, we need to declare a default value, similar to props:

js
// `value` przyjmie wartość "domyślna wartość"
// jeśli nie zostanie dodana wartość dla "message"
const value = inject('message', 'domyślna wartość')

W niektórych przypadkach wartość domyślna może wymagać utworzenia poprzez wywołanie funkcji lub utworzenie nowej instancji klasy. Aby uniknąć niepotrzebnych obliczeń lub efektów ubocznych w przypadku, gdy opcjonalna wartość nie jest używana, możemy użyć funkcji fabrykującej do utworzenia wartości domyślnej:

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

Trzeci parametr wskazuje, że wartość domyślna powinna być traktowana jako funkcja fabrykująca.

js
export default {
  // składnia obiektowa jest wymagana
  // podczas deklarowania wartości domyślnych dla wstrzyknięć
  inject: {
    message: {
      from: 'message', // jest to opcjonalne jeśli używamy tego samego klucza do wstrzyknięcia
      default: 'wartość domyślna'
    },
    user: {
      // użyj funkcji fabrykującej dla wartości nieprymitywnych, których utworzenie
      // jest kosztowne lub takich, które powinny być unikalne dla każdej instancji komponentu
      default: () => ({ name: 'John' })
    }
  }
}

Praca z reaktywnością ​

Podczas używania reaktywnych wartości provide / inject, zaleca się, aby wszelkie mutacje stanu reaktywnego były wykonywane wewnątrz dostawcy zawsze, gdy jest to możliwe. Zapewnia to, że dostarczany stan i jego możliwe mutacje są umiejscowione w tym samym komponencie, co ułatwia późniejsze utrzymanie kodu.

Mogą zdarzyć się sytuacje, gdy potrzebujemy zaktualizować dane z komponentu wstrzykującego. W takich przypadkach zalecamy dostarczenie funkcji, która jest odpowiedzialna za mutację stanu:

vue
<!-- wewnątrz komponentu dostarczającego -->
<script setup>
import { provide, ref } from 'vue'

const location = ref('North Pole')

function updateLocation() {
  location.value = 'South Pole'
}

provide('location', {
  location,
  updateLocation
})
</script>
vue
<!-- wewnątrz komponentu wstrzykującego -->
<script setup>
import { inject } from 'vue'

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

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

Ostatecznie, możesz opakować dostarczoną wartość za pomocą readonly() jeśli chcesz mieć pewność, że dane przekazywane przez provide nie mogą być mutowane przez komponent wstrzykujący.

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

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

Aby powiązać reaktywnie wstrzyknięcia z dostawcą, musimy dostarczyć właściwość obliczaną przy użyciu funkcji computed():

js
import { computed } from 'vue'

export default {
  data() {
    return {
      message: 'witaj!'
    }
  },
  provide() {
    return {
      // jawnie zdefiniuj właściwość obliczaną
      message: computed(() => this.message)
    }
  }
}

Pełny przykład provide + inject z reaktywnością

Funkcja computed() jest zazwyczaj używana w komponentach Composition API, ale może być również używana jako uzupełnienie niektórych przypadków użycia w Options API. Możesz dowiedzieć się więcej o jej zastosowaniu czytając Podstawy Reaktywności oraz Właściwości Obliczane z ustawieniem preferencji API na Composition API.

Praca z Symbolami jako kluczami ​

Do tej pory używaliśmy w przykładach kluczy wstrzykiwania jako ciągów znaków. Jeśli pracujesz nad dużą aplikacją z wieloma dostawcami zależności lub tworzysz komponenty, które będą używane przez innych programistów, najlepiej jest używać kluczy wstrzykiwania typu Symbol, aby uniknąć potencjalnych kolizji.

Zalecane jest eksportowanie Symboli w dedykowanym pliku:

keys.js
js
export const myInjectionKey = Symbol()
js
// w komopnencie dostarczającym
import { provide } from 'vue'
import { myInjectionKey } from './keys.js'

provide(myInjectionKey, {
  /* dane do wstrzyknięcia */
})
js
// w komponencie wstrzykującym
import { inject } from 'vue'
import { myInjectionKey } from './keys.js'

const injected = inject(myInjectionKey)

Zobacz także: Typowanie Provide / Inject (Dostarczanie / Wstrzykiwanie)

js
// w komponencie dostarczającym
import { myInjectionKey } from './keys.js'

export default {
  provide() {
    return {
      [myInjectionKey]: {
        /* dane do wstrzyknięcia */
      }
    }
  }
}
js
// w komponencie wstrzykującym
import { myInjectionKey } from './keys.js'

export default {
  inject: {
    injected: { from: myInjectionKey }
  }
}
Provide / Inject (Dostarczanie / Wstrzykiwanie)Jest załadowany