Vue.js: provide / inject

Última atualização: 2026-08-26

provide e inject são as APIs de comunicação entre níveis do Vue 3 — os componentes ancestrais fornecem dados, e qualquer componente descendente (independentemente da profundidade) pode injetá-los e utilizá-los. Isso resolve o problema do “prop drilling”.

O provide do Vue 3 oferece suporte a dados reativos (ref / reactive), e os componentes filhos se tornam automaticamente reativos após serem injetados. Esta lição ajudará você a dominar esse mecanismo e cinco casos de uso práticos.

1. O que você vai aprender


2. O pesadelo de perfurar com o Prop uma “cor temática” ao longo de 5 níveis

(1) Ponto crítico: Aplicativo → Layout → Cabeçalho → Menu do usuário → Botão, passagem por 5 camadas

O painel de administração da Alice precisava oferecer suporte à troca de temas:

VUE
<!-- ❌ The "Broken" Version: 5-Layer prop drilling -->
<!-- App.vue -->
<Layout :theme="theme" :user="user" :locale="locale">
  <router-view />
</Layout>

<!-- Layout.vue -->
<Header :theme="theme" :user="user" :locale="locale" />

<!-- Header.vue -->
<UserMenu :theme="theme" :user="user" :locale="locale" />

<!-- UserMenu.vue -->
<ThemeButton :theme="theme" />
<LocaleSelector :locale="locale" />

O gerente de produto Charlie adiciona mais 3 configurações:

“Alice, precisamos adicionar configurações para a barra lateral recolhida, as configurações de notificações e os sinalizadores de recurso. São 8 propriedades para passar por 5 componentes. Isso é impossível de manter.”

(2) Solução de fornecimento/injeção do Vue: injetar nos ancestrais; os descendentes recebem

VUE
<!-- App.vue Ancestors -->
<script setup>
import { ref, provide } from 'vue'

const theme = ref('light')
const user = ref({ name: 'Alice' })
const locale = ref('zh-CN')

// ✅ Once provide,All descendants may inject
provide('theme', theme)
provide('user', user)
provide('locale', locale)
</script>

<!-- UserMenu.vue Any descendant at any depth -->
<script setup>
import { inject } from 'vue'

// ✅ Get it directly, No props passthrough needed
const theme = inject('theme')
const user = inject('user')
const locale = inject('locale')
</script>

<template>
  <button :class="theme">Toggle {{ user.name }}</button>
  <p>{{ locale }}</p>
</template>

5 níveis de aninhamento1 linha de provide + 1 linha de inject.

(3) Receita

Após o fornecimento/injeção:


3. Sintaxe básica de provide e inject

(1) Forneça os dados de entrada

VUE
<!-- App.vue Ancestor Components -->
<script setup>
import { ref, provide } from 'vue'

const theme = ref('light')
const user = { name: 'Alice' }

// 1. Inject a static value
provide('appName', 'My Admin')

// 2. Injecting Responsive Data(ref)
provide('theme', theme)

// 3. Injecting Responsive Data(reactive)
provide('user', user)

// 4. Injection Methods
provide('updateUser', (newUser) => {
  user.value = newUser
})
</script>

(2) O inject recebe dados

VUE
<!-- Child.vue Any descendant component -->
<script setup>
import { inject } from 'vue'

// 1. Basic Reception
const theme = inject('theme')

// 2. With default values(My ancestors are no longer with us provide When to Use)
const theme = inject('theme', 'light')

// 3. Include factory function defaults
const theme = inject('theme', () => 'light')

// 4. Type Assertion(TypeScript)
const theme = inject<string>('theme', 'light')
</script>

(3) Exemplo completo

VUE
<!-- Ancestor.vue -->
<script setup>
import { ref, provide } from 'vue'

const count = ref(0)
const user = reactive({ name: 'Alice', age: 25 })

provide('count', count)        // ref
provide('user', user)          // reactive
provide('config', { theme: 'dark' })  // Object
provide('reset', () => { count.value = 0 })  // Methods
</script>

<!-- DeepChild.vue(Any depth) -->
<script setup>
import { inject } from 'vue'

const count = inject('count')        // Responsive
const user = inject('user')          // Responsive
const config = inject('config')      // Ordinary Object
const reset = inject('reset')        // Function

// Edit
function increment() {
  count.value++  // ✅ Ancestor components will also respond
}
</script>
▶ Experimente

4. O tipo “Symbol” como chave de injeção

(1) Por que usar o Symbol?

Chaves do tipo string estão sujeitas a conflitos (várias instruções provide com a mesma chave se sobrescreverão mutuamente). Os símbolos são únicos, o que evita conflitos.

VUE
<!-- keys.js - Centrally manage all provide key -->
<script>
export const THEME_KEY = Symbol('theme')
export const USER_KEY = Symbol('user')
export const LOCALE_KEY = Symbol('locale')
export const CONFIG_KEY = Symbol('config')
</script>
VUE
<!-- Ancestor.vue Ancestor Components -->
<script setup>
import { provide, ref } from 'vue'
import { THEME_KEY, USER_KEY, LOCALE_KEY } from './keys'

provide(THEME_KEY, ref('light'))
provide(USER_KEY, ref({ name: 'Alice' }))
provide(LOCALE_KEY, ref('zh-CN'))
</script>
VUE
<!-- DeepChild.vue Any descendants -->
<script setup>
import { inject } from 'vue'
import { THEME_KEY } from './keys'

// ✅ Type Safety:Must use Symbol talent inject
const theme = inject(THEME_KEY)
const user = inject(USER_KEY)
</script>

(2) 5 principais vantagens

Vantagem Descrição
Exclusividade O símbolo é globalmente exclusivo; não ocorrem conflitos
Segurança de tipos O TypeScript consegue inferir com precisão
Gerenciamento centralizado Todas as chaves são armazenadas em um único arquivo
Fácil de refatorar A modificação de uma chave não afeta as demais
Legibilidade Dá para saber para que serve só de olhar para o nome da chave

5. Uma explicação detalhada sobre o Responsive provide

(1) 3 tipos de modos de “fornecimento”

VUE
<!-- Ancestor.vue -->
<script setup>
import { ref, reactive, provide } from 'vue'

// 1. Inject ref(Responsive)
const count = ref(0)
provide('count', count)

// 2. Inject reactive(Responsive)
const user = reactive({ name: 'Alice' })
provide('user', user)

// 3. Injecting Read-Only Objects(Non-responsive)
const config = { theme: 'dark' }
provide('config', config)
</script>

(2) Duas maneiras de fazer alterações

VUE
<!-- Method 1:Directly Modify Ancestors(Recommendations) -->
<!-- Ancestor.vue -->
<script setup>
import { ref, provide } from 'vue'

const theme = ref('light')
provide('theme', theme)

function toggleTheme() {
  theme.value = theme.value === 'light' ? 'dark' : 'light'
}
</script>

<!-- Method 2:Through their descendants inject Edit Citation -->
<!-- DeepChild.vue -->
<script setup>
import { inject } from 'vue'

const theme = inject('theme')

function toggleTheme() {
  theme.value = 'dark'  // ✅ The change is ref,Both the ancestors and other descendants responded
}
</script>

(3) 5 principais cenários de resposta

Cenário Uso
Alterar tema ref + fornecer
Login do usuário reactive + provide
localização i18n referência + fornecimento + método de alternância
Carregamento global ref(false) + fornecer
Notificações de mensagens globais reativo + fornecer

6. 5 cenários-chave do mundo real

(1) Cena 1: Mudança de assunto

JS
// keys.js
export const THEME_KEY = Symbol('theme')
VUE
<!-- App.vue Ancestors -->
<script setup>
import { ref, provide } from 'vue'
import { THEME_KEY } from './keys'

const theme = ref('light')
provide(THEME_KEY, theme)
</script>

<!-- ThemeButton.vue Descendants -->
<script setup>
import { inject } from 'vue'
import { THEME_KEY } from './keys'

const theme = inject(THEME_KEY)
</script>

<template>
  <button @click="theme.value = theme === 'light' ? 'dark' : 'light'">
    {{ theme === 'light' ? '🌞' : '🌙' }}
  </button>
</template>

(2) Cenário 2: Autenticação do usuário

JS
// keys.js
export const AUTH_KEY = Symbol('auth')
VUE
<!-- App.vue -->
<script setup>
import { reactive, provide } from 'vue'
import { AUTH_KEY } from './keys'

const auth = reactive({
  user: null,
  isLoggedIn: false,
  login(credentials) { /* API call */ },
  logout() { this.user = null; this.isLoggedIn = false }
})
provide(AUTH_KEY, auth)
</script>

<!-- UserMenu.vue Descendants -->
<script setup>
import { inject } from 'vue'
import { AUTH_KEY } from './keys'

const auth = inject(AUTH_KEY)
</script>

<template>
  <div v-if="auth.isLoggedIn">{{ auth.user.name }}</div>
  <button v-else @click="auth.login(creds)">Login</button>
</template>

(3) Cenário 3: i18n

JS
// keys.js
export const I18N_KEY = Symbol('i18n')
VUE
<!-- App.vue -->
<script setup>
import { ref, provide, computed } from 'vue'
import { I18N_KEY } from './keys'

const messages = {
  'zh-CN': { hello: 'Hello', welcome: 'Welcome' },
  'en-US': { hello: 'Hello', welcome: 'Welcome' }
}

const locale = ref('zh-CN')
const t = computed(() => (key) => messages[locale.value][key])

provide(I18N_KEY, { locale, t })
</script>

<!-- Hello.vue Descendants -->
<script setup>
import { inject } from 'vue'
import { I18N_KEY } from './keys'

const { t } = inject(I18N_KEY)
</script>

<template>
  <h1>{{ t('hello') }}, {{ t('welcome') }}</h1>
</template>

(4) Cenário 4: Carregamento global

VUE
<!-- App.vue -->
<script setup>
import { ref, provide } from 'vue'

const loading = ref(false)
provide('loading', loading)
</script>

<!-- AnyComponent.vue Descendants -->
<script setup>
import { inject } from 'vue'
const loading = inject('loading')

async function fetchData() {
  loading.value = true
  await fetch('/api/data')
  loading.value = false
}
</script>

<template>
  <button @click="fetchData">Refresh</button>
</template>

(5) Cenário 5: Tema + Dados Empresariais

VUE
<!-- App.vue -->
<script setup>
import { ref, reactive, provide } from 'vue'

const appState = reactive({
  theme: 'light',
  user: { name: 'Alice' },
  permissions: ['read', 'write'],
  config: { sidebar: true }
})
provide('appState', appState)
</script>

7. provide / inject x outras abordagens

(1) Comparação entre 5 métodos de comunicação

Aspecto props/emit provide/inject barramento de eventos Pinia mitt
Correspondência entre pai e filho ⭐⭐⭐⭐⭐ - - - -
Entre níveis ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐
Brothers Communications ❌ (Requer a intervenção dos pais) ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐
Responsivo ❌ (Manual)
Segurança de tipos ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐
DevTools ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐
Curva de aprendizado ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐

(2) 5 cenários-chave de seleção

Cenário Solução recomendada
Comunicação entre pai e filho props/emit
Estado global entre níveis (tema/autenticação/i18n) fornecer/injetar
Estado global em aplicações de médio a grande porte Pinia (Fase 4.2)
Barramento de Eventos Simples mitt / tiny-emitter
Gerenciamento de estados complexos de negócios Pinia + Vue Router

8. Exemplo completo: como alternar temas no backend de um site de comércio eletrônico

▶ Exemplo: 1. Gerenciamento centralizado de chaves

JS
// src/keys.js
export const THEME_KEY = Symbol('theme')
export const USER_KEY = Symbol('user')
export const LOCALE_KEY = Symbol('locale')
export const CART_KEY = Symbol('cart')
▶ Experimente

▶ Exemplo: 2. App.vue — Única vez provide

VUE
<!-- src/App.vue -->
<script setup>
import { ref, reactive, provide, readonly } from 'vue'
import { THEME_KEY, USER_KEY, LOCALE_KEY, CART_KEY } from './keys'

// Topic
const theme = ref('light')
function setTheme(newTheme) {
  theme.value = newTheme
  localStorage.setItem('theme', newTheme)
}

// User
const user = reactive({
  id: 1,
  name: 'Alice',
  role: 'admin'
})

// Internationalization
const locale = ref('zh-CN')
const messages = {
  'zh-CN': { home: 'Home', cart: 'Shopping Cart' },
  'en-US': { home: 'Home', cart: 'Cart' }
}
const t = (key) => messages[locale.value][key]

// Shopping Cart
const cart = reactive({ items: [], total: 0 })

// Disposable provide 4 Global state
provide(THEME_KEY, { theme, setTheme })
provide(USER_KEY, { user })
provide(LOCALE_KEY, { locale, t })
provide(CART_KEY, { cart })
</script>

<template>
  <router-view />
</template>
▶ Experimente

▶ Exemplo: 3. Uso por qualquer descendente (5 maneiras de usá-lo)

VUE
<!-- DeepChild.vue Any depth -->
<script setup>
import { inject } from 'vue'
import { THEME_KEY, USER_KEY, LOCALE_KEY, CART_KEY } from './keys'

// 1. Deconstruction
const { theme, setTheme } = inject(THEME_KEY)
const { user } = inject(USER_KEY)
const { locale, t } = inject(LOCALE_KEY)
const { cart } = inject(CART_KEY)
</script>

<template>
  <div :class="['app', theme]">
    <p>{{ user.name }} ({{ user.role }})</p>
    <p>{{ t('home') }} | {{ t('cart') }}</p>
    <p>{{ cart.items.length }} items, ${{ cart.total }}</p>
    
    <button @click="setTheme('dark')">Dark Mode</button>
  </div>
</template>
▶ Experimente

▶ Exemplo: 4. Demonstração das 5 principais vantagens das chaves de injeção de símbolos

JS
// src/keys.js
export const THEME_KEY = Symbol('theme')  // The Only One
export const ANOTHER_THEME_KEY = Symbol('theme')  // Also the only one(Different Symbol)
▶ Experimente
VUE
<!-- App.vue -->
<script setup>
import { provide, ref } from 'vue'
import { THEME_KEY, ANOTHER_THEME_KEY } from './keys'

const theme1 = ref('light')
const theme2 = ref('dark')

// ✅ There will be no conflict:Different Symbol Even if the names are the same
provide(THEME_KEY, theme1)
provide(ANOTHER_THEME_KEY, theme2)
</script>

▶ Exemplo: 5. Referência rápida para 5 erros comuns

Erro Sintoma Solução
Conflitos de chaves de string Substituir Usar símbolo
Não é um ancestral indefinido Verificar hierarquia de componentes
Modificar valor somente leitura Aviso Modificar ancestral; os descendentes são somente leitura
Ignorar valor padrão indefinido inject(key, defaultValue)
Grande número de instruções provide Difícil de manter Centralizado em keys.js

▶ Exemplo: 6. 5 Principais comparações de desempenho

Modo Desempenho Aplicável
provide('key', value) ⭐⭐⭐⭐⭐ Valor estático
provide('key', ref) ⭐⭐⭐⭐⭐ Avaliação interativa
provide('key', reactive) ⭐⭐⭐⭐ Objetos complexos
provide('key', computed) ⭐⭐⭐⭐⭐ Valor derivado
provide('key', function) ⭐⭐⭐⭐ Como usar

❓ Perguntas Frequentes

P: O que devo usar, provide ou props? R: Use props para comunicação direta entre componentes pai e filho (explícita e segura em termos de tipos). Use provide para comunicação entre níveis (3 ou mais níveis) a fim de evitar a prop drilling. Use o Pinia (Fase 4.2) para o estado global em aplicações de grande porte.

P: Os dados fornecidos são reativos? R: Depende dos dados que estão sendo fornecidos. provide('key', ref) Reativos, provide('key', {a: 1}) Não reativos (objeto comum). Recomendamos usar provide ref ou provide reactive.

P: O provide precisa estar no nível superior do setup? R: Sim. O provide deve ser chamado no nível superior do <script setup> (não é permitido dentro de ganchos de ciclo de vida); caso contrário, o provide não terá efeito.

P: Posso modificar o valor de provide após inject? R: Você pode modificar ref.value, mas recomenda-se fazê-lo apenas em componentes ancestrais (usando o método provide). Modificar ref diretamente pode tornar o fluxo de dados pouco claro.

P: Onde está definida a tabela de símbolos? R: Em um arquivo separado src/keys.js, que exporta todos os símbolos. Todos os componentes utilizados são importados desse arquivo.

P: Como faço para escolher entre provide/inject e o Pinia? R: Para projetos pequenos (< 5 estados globais), o provide/inject é suficiente. Para projetos de médio a grande porte (10 ou mais estados globais), use o Pinia, pois ele oferece melhor suporte ao DevTools.

P: O que acontece quando um componente com provide é desmontado? R: Todos os componentes descendentes também são desmontados (ciclo de vida da árvore de componentes do Vue). inject irá procurar novamente pelos ancestrais na árvore.


📖 Resumo


📝 Exercícios

  1. Questões básicas (Dificuldade: ⭐)

    Implemente um seletor de temas simples:

    • keys.js Define THEME_KEY
    • O arquivo App.vue fornece o tema (ref.) + o método setTheme
    • ThemeButton.vue: injetar tema + definir tema
    • Clique no botão para alternar entre os modos claro e escuro
  2. Problemas avançados (Dificuldade: ⭐⭐)

    Implementar a autenticação do usuário (fornecer/injetar):

    • Injeção de AUTH_KEY: métodos user, login e logout
    • O arquivo App.vue inicializa user = null
    • No arquivo LoginPage.vue, chame login(creds) para simular um login
    • O arquivo Header.vue exibe uma interface de usuário diferente dependendo do valor de isLoggedIn
  3. Problema desafiador (Dificuldade: ⭐⭐⭐)

    Implementar um sistema abrangente de estado global para o back-end do comércio eletrônico:

    1. 4 teclas: TEMA / USUÁRIO / CARRINHO / CONFIGURAÇÃO REGIONAL
    2. O App.vue fornece quatro estados globais ao mesmo tempo
    3. Uso de 5 componentes em diferentes níveis (Cabeçalho / Barra lateral / Lista de produtos / Carrinho / Rodapé)
    4. Comunicação entre componentes: o cabeçalho aciona o logout → a barra lateral é atualizada automaticamente
    5. Troca de tema + Troca de internacionalização (i18n) + Adição e remoção de itens do carrinho de compras
    6. Uso do TypeScript para tipagem forte
    7. 5 Definir padrões (Value/ref/reactive/computed/function)
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%