Comunicação e Troca de Dados entre Componentes no Vue.js

  1. Fluxo de Dados de Pai para Filho

A transferência de informações de um componente pai para um filho é realizada através de propriedades (props). O pai vincula os dados como atributos, e o filho os declara para recebê-los.

<div id="application">
    <!-- Passando um valor estático -->
    <user-profile username="Visitante"></user-profile>
    
    <!-- Passando dados dinâmicos do estado do pai -->
    <user-profile :username="activeUser" :role="userRole"></user-profile>
</div>

<script>
  const app = Vue.createApp({
    data() {
      return {
        activeUser: 'Carlos Silva',
        userRole: 'Administrador'
      };
    }
  });

  app.component('user-profile', {
    props: ['username', 'role'],
    template: `<div class="card">
                 <h4>{{ username }}</h4>
                 <p>Nível de acesso: {{ role }}</p>
               </div>`
  });

  app.mount('#application');
</script>

  1. Comunicação de Filho para Pai

Para enviar dados na direção oposta, o componente filho emite eventos personalizados. O componente pai escuta esses eventos e executa funções de manipulação, recebendo os dados como argumentos.

<div id="application">
    <p :style="{ fontSize: textSize + 'px' }">Texto de exemplo</p>
    <control-panel :step="2" @adjust-size="modifySize($event)"></control-panel>
</div>

<script>
  const app = Vue.createApp({
    data() {
      return {
        textSize: 14
      };
    },
    methods: {
      modifySize(increment) {
        this.textSize += increment;
      }
    }
  });

  app.component('control-panel', {
    props: ['step'],
    template: `<button @click="triggerResize">Aumentar Fonte</button>`,
    methods: {
      triggerResize() {
        this.$emit('adjust-size', this.step);
      }
    }
  });

  app.mount('#application');
</script>

  1. Troca de Dados entre Componentes Irmãos

Quando componentes não possuem relação direta de hierarquia, um barramento de eventos (Event Bus) pode ser utilizado. Como o Vue 3 removeu a API de eventos da instância principle, implementamos um objeto pub/sub simples para gerenciar a comunicação de forma desacoplada.

<div id="application">
    <module-a></module-a>
    <module-b></module-b>
    <button @click="clearListeners">Limpar Eventos</button>
</div>

<script>
  const eventHub = {
    listeners: {},
    on(event, fn) { (this.listeners[event] = this.listeners[event] || []).push(fn); },
    emit(event, data) { (this.listeners[event] || []).forEach(fn => fn(data)); },
    off(event) { delete this.listeners[event]; }
  };

  const app = Vue.createApp({
    methods: {
      clearListeners() {
        eventHub.off('sync-data-a');
        eventHub.off('sync-data-b');
      }
    }
  });

  app.component('module-a', {
    data() { return { score: 0 }; },
    template: `<div>Módulo A: {{ score }} <button @click="sendData">Enviar para B</button></div>`,
    methods: {
      sendData() { eventHub.emit('sync-data-b', 5); }
    },
    mounted() {
      eventHub.on('sync-data-a', (val) => { this.score += val; });
    }
  });

  app.component('module-b', {
    data() { return { score: 0 }; },
    template: `<div>Módulo B: {{ score }} <button @click="sendData">Enviar para A</button></div>`,
    methods: {
      sendData() { eventHub.emit('sync-data-a', 3); }
    },
    mounted() {
      eventHub.on('sync-data-b', (val) => { this.score += val; });
    }
  });

  app.mount('#application');
</script>

  1. Injeção de Dependências com Provide e Inject

Para evitar a passagem excessiva de props por múltiplos níveis, o Vue oferece provide e inject. Um componente encestral fornece os dados, e qualquer descendente pode injetá-los diretamente em sua instância.

const app = Vue.createApp({});

app.component('settings-wrapper', {
  data() {
    return {
      themeConfig: { mode: 'dark', accent: 'blue' }
    };
  },
  provide() {
    return {
      activeTheme: Vue.computed(() => this.themeConfig.mode)
    };
  },
  template: `<div><slot></slot></div>`
});

app.component('deep-child-component', {
  inject: ['activeTheme'],
  template: `<p>Tema atual: {{ activeTheme }}</p>`
});

  1. Aprofundando em Props

Tipagem e Declaração

É possível definir tipos específicos para garantir a integridade dos dados recebidos e facilitar a depuração.

props: {
  identifier: Number,
  label: String,
  isVisible: Boolean,
  tags: Array,
  metadata: Object,
  onSubmit: Function
}

Fluxo Unidirecional

As props seguem um fluxo estrito de cima para baixo. O componente filho não deve mutar diretamente uma prop recebida. Se for necessário modificar o valor localmente, crie uma variável reativa no data inicializada com a prop, ou utilize uma computed property para transformações derivadas.

Validação Avançada

O framework permite configurações detalhadas para validar as props em tempo de desenvolvmiento, garantindo que os componentes sejam utilizados corretamente.

app.component('form-input', {
  props: {
    status: {
      type: String,
      required: true,
      validator: (value) => ['pending', 'approved', 'rejected'].includes(value)
    },
    limit: {
      type: Number,
      default: 50
    },
    config: {
      type: Object,
      default: () => ({ retry: false, timeout: 3000 })
    }
  }
});

Nomenclatura (CamelCase vs Kebab-case)

Os atributos HTML não diferenciam maiúsculas de minúsculas. Portanto, ao utilizar templates diretamente no DOM, propriedades declaradas em camelCase no JavaScript devem ser escritas em kebab-case no HTML.

app.component('data-grid', {
  props: ['maxRowCount'],
  template: '<span>Linhas: {{ maxRowCount }}</span>'
});

// No template HTML:
// <data-grid max-row-count="100"></data-grid>

Tags: vuejs component-communication props event-bus provide-inject

Publicado em 7-16 03:37