Fluxo de Dados Unidirecional no Vue.js
No Vue.js, os dados fluem em uma única direção, do componente pai para os componentes filhos, por meio de props
. Quando o componente pai altera um valor, os componentes filhos que recebem esse valor por props
são atualizados automaticamente. No entanto, um componente filho não pode modificar diretamente uma prop, pois props
são apenas para leitura.
Se um componente filho tentar alterar diretamente um valor recebido por props
, o Vue emitirá um aviso no console do navegador e a alteração não será aplicada.
No exemplo abaixo, um valor é passado do componente pai para o filho, que tenta modificá-lo por meio de um campo de entrada:
<!DOCTYPE html>
<html>
<head>
<title>Componentes Vue.js</title>
<meta charset="utf-8" />
<script src="https://unpkg.com/vue"></script>
</head>
<body>
<div id="app">
<h2>Hello, {{ name }}</h2>
<user-edit :user="name"></user-edit>
</div>
<script>
const app = Vue.createApp({
data() {
return { name: "Tom" };
},
});
app.component("user-edit", {
props: ["user"],
template: '<div><input type="text" v-model="user" /><p>Name: {{ user }}</p></div>',
});
app.mount("#app");
</script>
</body>
</html>
O componente user-edit
recebe o valor name
por meio de props
. O campo de entrada está vinculado a esse valor com v-model
, o que sugere que ele pode ser alterado. No entanto, como props
são apenas para leitura, qualquer tentativa de alteração resultará em um aviso no console do navegador.
O componente user-edit
recebe o valor name
por meio de props
. O campo de entrada está vinculado a esse valor com v-model
, o que sugere que ele pode ser alterado. No entanto, como props
são apenas para leitura, qualquer tentativa de alteração resultará em um aviso no console do navegador.
Se for necessário permitir que o componente filho modifique o valor recebido por props
, a solução é criar uma variável local dentro do data()
, que recebe o valor inicial de props
, mas não o modifica diretamente:
<!DOCTYPE html>
<html>
<head>
<title>Componentes Vue.js</title>
<meta charset="utf-8" />
<script src="https://unpkg.com/vue"></script>
</head>
<body>
<div id="app">
<h2>Hello, {{ name }}</h2>
<user-edit :user="name"></user-edit>
</div>
<script>
const app = Vue.createApp({
data() {
return { name: "Tom" };
},
});
app.component("user-edit", {
props: ["user"],
data() {
return { userName: this.user };
},
template: '<div><input type="text" v-model="userName" /><p>Name: {{ userName }}</p></div>',
});
app.mount("#app");
</script>
</body>
</html>
Agora, todas as modificações feitas no campo de entrada afetam apenas a variável userName
dentro do componente user-edit
, sem alterar o valor original recebido por props
.
Sincronizando Valores Entre Pai e Filho
A abordagem acima resolve o problema de tentar alterar diretamente uma prop em um componente filho. No entanto, ela apresenta um problema: se o valor da prop user
for atualizado pelo componente pai, userName
dentro do componente filho não refletirá essa mudança automaticamente.
Se for necessário manter os valores sincronizados quando o componente pai atualiza uma prop, a melhor abordagem é usar um watcher (watch
). Ele observa mudanças no valor de props
e atualiza a variável local quando necessário:
<!DOCTYPE html>
<html>
<head>
<title>Componentes Vue.js</title>
<meta charset="utf-8" />
<script src="https://unpkg.com/vue"></script>
</head>
<body>
<div id="app">
<div>
<h3>Vue App</h3>
<input type="text" v-model="name" />
<p>Hello, {{ name }}</p>
</div>
<user-edit :user="name"></user-edit>
</div>
<script>
const app = Vue.createApp({
data() {
return { name: "Tom" };
},
});
app.component("user-edit", {
props: ["user"],
data() {
return { userName: this.user };
},
watch: {
user(newVal) {
this.userName = newVal;
},
},
template: '<div><h3>Component</h3><input type="text" v-model="userName" /><p>User name: {{ userName }}</p></div>',
});
app.mount("#app");
</script>
</body>
</html>
O watch
monitora a prop user
. Sempre que o valor dela mudar no componente pai, o valor de userName
dentro do componente filho é atualizado para refletir essa mudança. Isso mantém a sincronização entre o estado do componente pai e o estado interno do componente filho.
Documentação oficial: