Como passar dados entre componentes no Vue.js

Publicados: 2022-03-10
Resumo rápido ↬ Com tantas maneiras diferentes de compartilhar dados entre componentes, você deve saber qual técnica é melhor para sua situação. Vamos analisar três das formas mais comuns de passar dados no VueJS.

O compartilhamento de dados entre componentes é uma das principais funcionalidades do VueJS. Ele permite que você crie um projeto mais modular, controle escopos de dados e crie um fluxo natural de dados em seu aplicativo.

A menos que você esteja criando todo o seu aplicativo Vue em um componente (o que não faria nenhum sentido), você encontrará situações em que precisa compartilhar dados entre componentes.

Ao final deste tutorial, você conhecerá três maneiras de fazer isso.

  • Usando adereços para compartilhar dados de pai para filho,
  • Emitindo eventos personalizados para compartilhar dados de filho para pai,
  • Usando o Vuex para criar um estado compartilhado no nível do aplicativo.

Ok - vamos direto ao assunto!

Construindo um aplicativo com Nuxt

Com o Spotify, seus amigos podem conferir o que você está tocando. E se o resto da Internet também pudesse experimentar o seu algoritmo? Aprenda a compor seu próprio aplicativo para compartilhar o que você está ouvindo no Spotify usando Vue.js e Nuxt. Leia um artigo relacionado →

Mais depois do salto! Continue lendo abaixo ↓

1. Usando adereços para compartilhar dados de pai para filho

As props VueJS são a maneira mais simples de compartilhar dados entre componentes. Props são atributos personalizados que podemos dar a um componente. Então, em nosso modelo, podemos fornecer valores a esses atributos e — BAM — estamos passando dados de um componente pai para um componente filho!

Por exemplo, digamos que estamos trabalhando em uma página de perfil de usuário e queremos que um componente filho aceite uma prop de nome de usuário. Vamos precisar de dois componentes.

  1. O componente filho aceitando o prop, vamos chamar isso de AccountInfo.vue .
  2. O componente pai passando o prop, vamos chamar isso de ProfilePage.vue .

Dentro de AccountInfo.vue , podemos declarar os props que ele aceita usando a opção props. Então, dentro das opções do componente, vamos fazer com que fique parecido com o seguinte.

 // AccountInfo.vue <template> <div id='account-info'> {{username}} </div> </template> <script> export default { props: ['username'] } </script>

Então, para realmente passar os dados do pai ( ProfilePage.vue ), passamos como um atributo personalizado.

 // ProfilePage.vue <account-info username='matt' />

Agora, se carregarmos nossa página, podemos ver que nosso componente AccountInfo renderiza corretamente o valor passado por seu pai.

Como ao trabalhar com outras diretivas VueJS, podemos usar v-bind para passar props dinamicamente. Por exemplo, digamos que queremos definir a prop username para ser igual a uma variável. Podemos fazer isso usando um atalho para a diretiva v-bind (ou apenas : para abreviar). O código ficaria um pouco assim:

 <template> <div> <account-info :username="user.username" /> </div> </template> <script> import AccountInfo from "@/components/AccountInfo.vue"; export default { components: { AccountInfo }, data() { return { user: { username: 'matt' } } } } </script>

Isso significa que podemos alterar nossos dados e fazer com que qualquer props filho usando esse valor também seja atualizado.

Dica: sempre verifique seus adereços

Se você deseja escrever um código Vue mais claro, uma técnica importante é verificar suas props. Resumindo, isso significa que você precisa especificar os requisitos para sua prop (ou seja, tipo, formato e assim por diante). Se um desses requisitos não for atendido (por exemplo, se o prop for passado de um tipo incorreto), o Vue imprimirá um aviso.

Digamos que queremos que nosso prop de nome de usuário aceite apenas Strings. Teríamos que modificar nosso objeto props para ficar assim:

 export default { props: { username: String } }

A verificação de props é essencial ao trabalhar em aplicativos Vue de grande escala ou ao projetar plugins. Isso ajuda a garantir que todos estejam na mesma página e usem adereços da maneira que foram planejados.

Para obter uma lista completa das verificações que podemos incluir nos adereços, definitivamente recomendo verificar a documentação oficial para uma revisão detalhada.

Dica: siga as convenções de nomenclatura de props

De acordo com o guia de estilo VueJS, a melhor maneira de nomear seus props é usando camelCase ao declará-los em seu script e kebab-case ao referenciá-los no código do modelo.

O raciocínio por trás disso é realmente muito simples. Em Javascript, camelCase é a convenção de nomenclatura padrão e em HTML, é kebab-case.

Portanto, o Vue recomenda que sigamos as normas de cada idioma. Felizmente, o Vue é capaz de converter automaticamente entre os dois estilos, portanto, não há configuração adicional para os desenvolvedores.

 // GOOD <account-info :my-username="user.username" /> props: { myUsername: String } // BAD <account-info :myUsername="user.username" /> props: { "my-username": String }

2. Emissão de eventos para compartilhar dados de filho para pai

Agora que temos dados passando pela hierarquia, vamos passar de outra forma: de um componente filho para um pai. Não podemos usar adereços, mas podemos usar eventos e ouvintes personalizados.

Cada instância do Vue pode chamar um método .$emit(eventName) que aciona um evento. Então, podemos escutar este evento da mesma forma que qualquer outro, usando a diretiva v-on.

Criando um evento personalizado

Vamos desenvolver nosso exemplo de perfil de usuário adicionando um botão que altera o nome de usuário. Dentro do nosso componente filho ( AccountInfo.vue ), vamos criar o botão.

Então, quando este botão for clicado, emitiremos um evento chamado changeUsername .

 <template> <div id='account-info'> <button @click='changeUsername()'>Change Username</button> {{username}} </div> </template> <script> export default { props: { username: String }, methods: { changeUsername() { this.$emit('changeUsername') } } } </script>

Dentro do pai, tratamos este evento e alteramos a variável user.username . Como estávamos discutindo anteriormente, podemos ouvir eventos usando a diretiva v-on ou "@" para abreviar.

 <template> <div> <account-info :username="user.username" @changeUsername="user.username = 'new name'"/> </div> </template>

Vamos experimentá-lo. Você deve ver que quando você clica no botão, o nome de usuário muda para "novo nome".

Dica: eventos personalizados podem aceitar argumentos

O caso de uso mais comum para passar argumentos para seus eventos é quando você deseja que um componente filho possa definir um valor específico para sua prop. Você nunca quer editar diretamente o valor de uma prop do próprio componente.

No entanto, felizmente, podemos usar argumentos de passagem com nossos eventos personalizados para fazer com que o componente pai altere os valores.

Digamos que queremos modificar o evento changeUsername para que possamos passar um valor.

O método $emit recebe um segundo parâmetro opcional para argumentos. Então, tudo o que fazemos é adicionar nosso novo valor de nome de usuário após o nome do nosso evento.

 this.$emit('changeUsername', 'mattmaribojoc')

Então, em nosso componente pai, podemos acessar esses valores inline usando uma variável $event especial ou podemos escrever um método manipulador que recebe um parâmetro.

 <account-info :username="user.username" @changeUsername="user.username = $event"/> OR <account-info :username="user.username" @changeUsername="changeUsername($event)"/> export default { ... methods: { changeUsername (username) { this.user.username = username; } } }

3. Usando Vuex para criar um estado compartilhado em nível de aplicativo

Ok - sabemos como compartilhar dados entre pais/filhos, mas e os outros componentes? Temos que criar um sistema de hierarquia extremamente complexo se quisermos passar dados?

Felizmente não. A maravilhosa biblioteca de gerenciamento de estado Vuex vem simplificando a vida dos desenvolvedores há anos. Em resumo, ele cria um armazenamento de dados centralizado que pode ser acessado por todos os componentes.

Nos métodos que usamos anteriormente (props/emitting events), cada componente tem seu próprio estado de dados que compartilhamos entre os componentes. No entanto, o Vuex nos permite extrair todos os dados compartilhados em um único estado que cada componente pode acessar facilmente. Esse estado compartilhado é chamado de armazenamento.

Vamos experimentá-lo.

Como o Vuex é separado do código principal do Vue, primeiro teremos que instalá-lo e importá-lo para nosso projeto. Primeiro, teremos que executar npm install vuex --save dentro da CLI do nosso projeto.

Em seguida, crie uma pasta src/store com um arquivo index.js que contém o código a seguir.

 // store/index.js import Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex); export default new Vuex.Store({ state: {}, getters: {}, mutations: {}, actions: {} });

Para incluir isso em nossa instância raiz do Vue, temos que importar nosso arquivo store/index.js e passá-lo em nosso construtor Vue.

 // main.js import store from "./store"; new Vue({ store, ...

Acessando os componentes internos do Vue Store

Como adicionamos nossa loja Vuex em nossa instância Vue raiz, ela é injetada em todos os filhos da raiz. Se quisermos acessar a loja de um componente, podemos através de this.$store .

Agora, vamos mergulhar nas especificidades de cada uma das quatro partes de uma loja Vuec.

1. Estado

O estado Vuex é um objeto que contém dados em nível de aplicativo. Todas as instâncias do Vue poderão acessar esses dados.

Para nossa loja, vamos criar um objeto de usuário que armazena mais alguns dados de perfil de usuário.

 export default new Vuex.Store({ state: { user: { username: 'matt', fullName: 'Matt Maribojoc' } }, getters: {}, mutations: {}, actions: {} });

Podemos acessar esses dados dentro de qualquer componente de instância como este.

 mounted () { console.log(this.$store.state.user.username); },

2. Getters

Usamos getters Vuex para retornar um valor modificado de dados de estado. Uma boa maneira de pensar em getters é tratá-los como propriedades computadas. Por exemplo, getters, como propriedades computadas, armazenam seus resultados em cache e só reavaliam quando uma dependência é modificada.

Com base em nossa loja anterior, digamos que queremos criar um método que retorne o primeiro nome de um usuário com base no atributo de nome completo.

 getters: { firstName: state => { return state.user.fullName.split(' ')[0] } }

As propriedades getter do Vuex estão disponíveis para componentes no objeto store.getters .

 mounted () { console.log(this.$store.getters.firstName); }

Dica: conheça os argumentos getter padrão

Por padrão, os getters Vuex aceitam dois argumentos.

  1. state — o objeto de estado para nosso aplicativo;
  2. getters — o objeto store.getters, significando que podemos chamar outros getters em nossa loja.

Cada getter que você declarar exigirá o primeiro argumento de estado. E dependendo de como você projeta seu código, seus getters podem fazer referência uns aos outros usando o segundo argumento 'getters'.

Vamos fazer um getter de sobrenome que simplesmente remova nosso valor de primeiro nome de nossa propriedade de estado de nome completo. Este exemplo exigiria os objetos state e getters.

 lastName (state, getters) { return state.user.fullName.replace(getters.firstName, ''); }

Dica: Passe Argumentos Personalizados para Getters Vuex

Outro recurso interessante dos getters é que podemos passar argumentos personalizados para eles fazendo com que nosso getter retorne um método.

 prefixedName: (state, getters) => (prefix) => { return prefix + getters.lastName; } // in our component console.log(this.$store.getters.prefixedName("Mr."));

3. Mutações

As mutações são a única maneira de alterar adequadamente o valor do objeto de estado. Um detalhe importante a ser observado é que as mutações devem ser síncronas .

Assim como os getters, as mutações sempre aceitam a propriedade de estado Vuex como seu primeiro argumento. Eles também aceitam um argumento personalizado — chamado de carga útil — como segundo argumento.

Por exemplo, vamos fazer uma mutação para alterar o nome de um usuário para um valor específico.

 mutations: { changeName (state, payload) { state.user.fullName = payload } },

Então, podemos chamar esse método de nosso componente usando o método store.commit , com nosso payload como segundo argumento.

 this.$store.commit("changeName", "New Name");

Na maioria das vezes, você vai querer que sua carga útil seja um objeto. Isso não apenas significa que você pode passar vários argumentos para uma mutação, mas também torna seu código mais legível por causa dos nomes de propriedade em seu objeto.

 changeName (state, payload) { state.user.fullName = payload.newName }

Existem duas maneiras diferentes de chamar mutações com uma carga útil.

  1. Você pode ter o tipo de mutação como o primeiro argumento e a carga como o segundo.
  2. Você pode declarar passar um único objeto, com uma propriedade para o tipo e outra para a carga.
 this.$store.commit("changeName", { newName: "New Name 1", }); // or this.$store.commit({ type: "changeName", newName: "New Name 2" });

Não há uma diferença real entre como os dois funcionam, então depende totalmente da preferência pessoal. Lembre-se de que é sempre melhor ser consistente ao longo de todo o seu projeto, então, seja qual for a sua escolha, fique com ele!

4. Ações

No Vuex, as ações são bastante semelhantes às mutações porque as usamos para alterar o estado. No entanto, as ações não alteram os valores em si. Em vez disso, as ações cometem mutações.

Além disso, enquanto as mutações do Vuex precisam ser síncronas, as ações não. Usando ações, podemos chamar uma mutação após uma chamada de API, por exemplo.

Enquanto a maioria dos manipuladores Vuex que vimos aceitam o estado como seu parâmetro principal, as ações aceitam um objeto de contexto. Este objeto de contexto nos permite acessar as propriedades em nossa loja Vuex (por exemplo, state, commit, getters).

Aqui está um exemplo de uma ação Vuex que espera dois segundos e, em seguida, confirma a mutação changeName .

 actions: { changeName (context, payload) { setTimeout(() => { context.commit("changeName", payload); }, 2000); } }

Dentro de nossos componentes, usamos o método store.dispatch para executar nossa função. Passamos argumentos exatamente como fizemos com mutações. Declaramos o tipo e passamos quaisquer argumentos personalizados no segundo argumento.

 this.$store.dispatch("changeName", { newName: "New Name from Action" });

Empacotando

Agora, você deve conhecer três maneiras diferentes de compartilhar dados entre componentes no VueJS: props, eventos personalizados e uma loja Vuex.

Espero que este tutorial tenha ajudado a fornecer mais informações sobre alguns métodos e práticas recomendadas diferentes do Vue. Deixe-me saber como você os implementou em seus projetos!

Leitura adicional

Se você estiver interessado em aprofundar ainda mais o lado técnico/capacidades de cada técnica, aqui estão alguns ótimos lugares para começar.

  • Site oficial do guia Vuex
  • Documentos VueJS para adereços e eventos personalizados
  • “WTF é Vuex? Um guia para iniciantes no armazenamento de dados de aplicativos do Vue”, Anthony Gore, desenvolvedores do Vue.js