No meu último post, falei sobre como o Vue executa funções de template e watch
em lote. Porém, existe outra forma de reatividade no Vue: as propriedades computadas, usando o computed
. O computed
serve para estados derivados, aplicando operações sobre uma ou mais refs
.
O computed
também é um efeito, assim como watch
e funções de template, mas possui sua própria regra de execução: ele só é reavaliado sob demanda.
<script setup>
import { ref, computed } from "vue";
const msg = ref("Hello World!");
const showTitle = ref(false);
const upper = computed(() => {
console.log("computed");
return msg.value.toUpperCase();
});
</script>
<template>
<div>
<button @click="showTitle = !showTitle">Toggle Title</button>
</div>
<input v-model="msg" />
<h1 v-if="showTitle">{{ upper }}</h1>
</template>
Note no exemplo que a função de template lê o valor de upper
apenas se showTitle
for true
.
Experimente esconder o título e modificar o texto: o computed
só será executado (e apenas uma vez)
quando você mostrar o título novamente.
E como já sabemos que o Vue reexecuta a função de template em lote, mesmo que upper
dependa de msg
,
ele só será recalculado quando a função de template for executada.
<script setup>
import { ref, computed } from "vue";
const msg = ref("Hello World!");
function newWords() {
msg.value += " ";
msg.value += "Hello";
msg.value += " ";
msg.value += "World";
msg.value += "!";
}
const upper = computed(() => {
console.log("computed");
return msg.value.toUpperCase();
});
</script>
<template>
<input v-model="msg" />
<button @click="newWords">More words</button>
<h1>{{ upper }}</h1>
</template>
Note que nesse caso não é o upper
que está sendo executado em lote, e sim a função de template.
Se você acessar o valor de upper
dentro da função newWords
, a função do computed
será reexecutada a cada acesso.
function newWords() {
msg.value += " ";
console.log(upper.value); // upper será executado
msg.value += "Hello";
console.log(upper.value); // upper será executado
msg.value += " ";
console.log(upper.value); // upper será executado
msg.value += "World";
console.log(upper.value); // upper será executado
msg.value += "!";
console.log(upper.value); // upper será executado
}
No entanto, o Vue também inclui outra otimização: cache. Se as dependências do computed
não mudaram,
a função não será reexecutada, independentemente da quantidade de acessos.
<script setup>
import { ref, computed } from "vue";
const msg = ref("Hello World!");
const savedUpperMsg = ref([]);
function saveMsg() {
savedUpperMsg.value.push(upper.value);
console.log("adding to the list:", upper.value);
}
const upper = computed(() => {
console.log("computed");
return msg.value.toUpperCase();
});
</script>
<template>
<input v-model="msg" />
<button @click="saveMsg">Save Msg</button>
<div v-for="msg in savedUpperMsg">
{{ msg }}
</div>
</template>
Note que, mesmo acessando o valor de upper
várias vezes, se o valor de msg
não mudou,
a função do computed
não é executada novamente.
Conclusão
O Vue oferece otimizações automáticas para propriedades computed
que garantem eficiência ao evitar
execuções desnecessárias. Isso permite que você foque na lógica da sua aplicação sem se preocupar
com performance.
Para aproveitar ao máximo esses benefícios:
- Mantenha seus
computed
sempre puros (sem efeitos colaterais) - Evite operações custosas ou assíncronas dentro de
computed
- Use
computed
para dados derivados, em vez de múltiplasrefs
ewatch