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

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:

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