- 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>
- 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>
- 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>
- 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>`
});
- 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>