Skip to content

Fornecer / Injetar

Esta página presume que já fizeste leitura dos Fundamentos de Componentes. Leia aquele primeiro se fores novo para os componentes.

Perfuração de Propriedade

Usualmente, quando precisamos passar dados do componente pai para um componente filho, utilizamos as propriedades. No entanto, imagine o caso onde temos uma grande árvore de componente, e um componente encaixado profundamente precisa de alguma coisa de um componente ancestral distante. Apenas com as propriedades, teríamos de passar a mesmo propriedade através da corrente do componente pai inteira:

diagrama da perfuração de propriedade

Repara que apesar do componente <Footer> pode não importar-se com estas propriedades absolutamente, ele ainda precisa declarar e passá-los exatamente juntos assim o <DeepChild> pode acessá-los. Se existir uma corrente pai mais longa, mais componentes seriam afetadas ao longo do caminho. Isto é chamado "perfuração de propriedade" e definitivamente não é divertido de se lidar.

Nós podemos resolver a perfuração de propriedades com provide e inject. Um componente pai pode servir como um fornecedor de dependência para todos os seus descendentes. Qualquer componente na árvore de descendência, independentemente de quão profundo ele esteja, pode injetar as dependências fornecidas pelos componentes para cima na corrente do seu componente pai.

Esquema Fornecer/Injetar

Fornecer

Para fornecer dados aos descendentes do componente, utilize a função provide():

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

provide(/* chave */ 'message', /* valor */ 'hello!')
</script>

Se estiveres utilizando o <script setup>, certifica-te de que provide() seja chamada de maneira síncrona dentro da setup():

js
import { provide } from 'vue'

export default {
  setup() {
    provide(/* chave */ 'message', /* valor */ 'hello!')
  }
}

A função provide() aceita dois argumentos. O primeiro argumento é chamado de chave da injeção, a qual pode ser uma sequência de caracteres ou um Symbol. A chave da injeção é utilizada pelos componentes descendentes para pesquisar o valor desejado para injetar. Um único componente pode chamar provide() várias vezes com chaves de injeção diferentes para fornecer valores diferentes.

O segundo argumento é o valor fornecido. O valor pode ser de qualquer tipo, incluindo estado reativo tais como referências:

js
import { ref, provide } from 'vue'

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

O fornecimento de valores reativos permite os componentes descendentes utilizar o valor fornecido para estabelecer uma conexão reativa para componente fornecedor.

Para fornecer dados aos descendentes do componente, utilize a opção provide:

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

Para cada propriedade no objeto provide, a chave está utilizado pelos componentes filho para localizar o valor correto para injetar, enquanto o valor é o que acaba sendo injetado.

Se precisarmos fornecer o estado por instância, por exemplo os dados declarado através da data(), então provide deve utilizar um valor de função:

js
export default {
  data() {
    return {
      message: 'hello!'
    }
  },
  provide() {
    // utiliza a sintaxe de função para possamos acessar o `this`
    return {
      message: this.message
    }
  }
}

No entanto, anote que isto não torna a injeção reativa. Nós discutiremos tornando injeções reativas abaixo.

Fornecimento a Nível de Aplicação

Além do fornecimento de dados em um componente, podemos também fornecer no nível da aplicação:

js
import { createApp } from 'vue'

const app = createApp({})

app.provide(/* chave */ 'message', /* valor */ 'hello!')

Os fornecimentos de nível da aplicação estão disponíveis para todos os componentes interpretados na aplicação. Isto é especialmente útil quando estamos escrevendo extensões, visto que as extensões normalmente não seriam capazes de fornecer valores utilizando componentes.

Injetar

Para injetar os dados fornecidos por um componente ancestral, utilize a função inject():

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

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

Se o valor fornecido for uma referência, ela será injetada como está e não será automaticamente desembrulhada. Isto permite o componente injetor conservar a conexão de reatividade para o componente fornecedor.

Exemplo de fornecer + injetar com Reatividade completo

Novamente, se não estiveres utilizando <script setup>, inject() deve apenas ser chamada de maneira síncrona dentro de setup():

js
import { inject } from 'vue'

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

Para injetar os dados fornecidos por um componente ancestral, utilize a opção inject:

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

As injeções são resolvidas antes do estado do próprio componente, assim podes acessar as propriedades injetas na data():

js
export default {
  inject: ['message'],
  data() {
    return {
      // dados iniciais baseados no valor injetado
      fullMessage: this.message
    }
  }
}

Exemplo de fornecer + injetar completo

Pseudónimo da Injeção *

Quando estiveres utilizando uma sintaxe de arranjo para a inject, as propriedades injetadas são expostas sobre a instância do componente utilizando a mesma chave. No exemplo acima, a propriedade foi fornecida sob a chave "message", e injetados como this.message. A chave local é a mesma que a chave da injeção.

Se queremos injetar a propriedades utilizando uma chave local diferente, precisamos utilizar a sintaxe de objeto para a opção inject:

js
export default {
  inject: {
    /* chave local */ localMessage: {
      from: /* chave da injeção */ 'message'
    }
  }
}

Aqui, o componente localizará a propriedade fornecida com a chave "message", e então a exporá como this.localMessage.

Valores Padrão da Injeção

Por padrão, a inject presume que a chave injetada é fornecida em algum lugar na corrente do componente pai. No caso onde a chave não é fornecida, haverá um aviso de tempo de execução.

Se queremos fazer uma propriedade injetada funcionar com fornecedores opcionais, precisamos declarar um valor padrão, semelhante as propriedades:

js
// `value` será "default value"
// se nenhum dado correspondendo "message" foi passado
const value = inject('message', 'default value')

Em alguns casos, o valor padrão pode precisar ser criado chamando uma função ou instanciando uma nova classe. Para evitar cálculo desnecessário ou efeitos colaterais no caso do valor opcional não for passado, podemos utilizar uma função de fábrica ("factory function", se preferires) para criação do valor padrão:

js
const value = inject('key', () => new ExpensiveClass())
js
export default {
  // a sintaxe de objeto é obrigatória
  // quando estiveres declarando valores padrão para injeções
  inject: {
    message: {
      from: 'message', // isto é opcional se estiveres utilizando a mesma chave para injeção
      default: 'default value'
    },
    user: {
      // utilize uma função de fábrica para valores não primitivos que são caros
      // de criar, ou aqueles que deveriam ser únicos por instância de componente.
      default: () => ({ name: 'John' })
    }
  }
}

Trabalhando com Reatividade

Quando estiveres utilizando os valores reativos de fornecer e injetar, é recomendado manter quaisquer mutações para o estado reativo dentro do fornecedor sempre que possível. Isto garante que o estado fornecido e suas possíveis mutações são co-localizados no mesmo componente, tornando mais fácil manter no futuro.

Talvez exista momentos em que precisamos atualizar os dados a partir de um componente injetor. Em tais casos, recomendamos o fornecimento de uma função que seja responsável pela mutação do estado:

vue
<!-- dentro do componente fornecedor -->
<script setup>
import { provide, ref } from 'vue'

const location = ref('North Pole')

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

provide('location', {
  location,
  updateLocation
})
</script>
vue
<!-- no componente injetor -->
<script setup>
import { inject } from 'vue'

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

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

Finalmente, podes envolver o valor fornecido com a readonly() se quiseres garantir que os dados passados através de provide não possam ser alterados pelo componente injetado.

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

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

Para tornar as injeções ligadas de maneira reativa ao fornecedor, precisamos fornecer um propriedade computada utilizando a função computed():

js
import { computed } from 'vue'

export default {
  data() {
    return {
      message: 'hello!'
    }
  },
  provide() {
    return {
      // fornecer explicitamente uma propriedade computada
      message: computed(() => this.message)
    }
  }
}

Exemplo completo sobre fornecer + injetar com Reatividade

A função computed() é normalmente utilizada nos componentes da API de Composição, mas podem também ser utilizadas para complementar certos casos de uso na API de Opções. Tu podes aprender mais a respeito disto lendo os Fundamentos de Reatividade e as Propriedades Computadas com a Preferência de API definida para API de Composição.

Trabalhando com Chaves de Symbol

Até aqui, temos estado utilizando chaves de injeção de sequência de caracteres nos exemplos. Se estiveres trabalhando em uma aplicação grande com vários provedores de dependência, ou estiveres criando componentes que serão utilizados por outros programadores, é melhor utilizar as chaves de injeção de Symbol ("Símbolo") para evitar potenciais colisões.

É recomendado exportar os símbolos em um ficheiro dedicado:

js
// keys.js
export const myInjectionKey = Symbol()
js
// no componente fornecedor
import { provide } from 'vue'
import { myInjectionKey } from './keys.js'

provide(myInjectionKey, {
  /* dados a fornecer */
})
js
// no componente injetor
import { inject } from 'vue'
import { myInjectionKey } from './keys.js'

const injected = inject(myInjectionKey)

Consulte também: Tipando provide / inject

js
// no componente fornecedor
import { myInjectionKey } from './keys.js'

export default {
  provide() {
    return {
      [myInjectionKey]: {
        /* dados a fornecer */
      }
    }
  }
}
js
// no componente injetor
import { myInjectionKey } from './keys.js'

export default {
  inject: {
    injected: { from: myInjectionKey }
  }
}
Fornecer / Injetar has loaded