As ferramentas em JavaScript vêm com mecanismos de reatividade. Ao saber o que significa reatividade e como funciona internamente, você pode entender quando seu código funciona e falha. Além de ser capaz de identificar bugs e evitar que eles aconteçam.
Vamos começar examinando a palavra reatividade. O que isso significa?
Anos atrás, os desenvolvedores implementaram HTML puro com JavaScript para obter interações na página. A reatividade no frontend era "arcaica", mas o suficiente para a época.
Devido ao aumento da complexidade nas páginas e aplicativos, esta solução era difícil de manter. Foi quando surgiram as bibliotecas e frameworks, como o Vue.
Em vez de programar diretamente a interação do DOM, essas ferramentas lidam com esse trabalho, enquanto provam uma maneira muito mais limpa de estruturar seu projeto.
Definindo reatividade na comunidade de front-end, ele sempre será anexado a frameworks, como React e Vue. O que eu quero compartilhar aqui não são os frameworks no frontend. E em vez disso, como as coisas funcionam com o Vue.
Este é um código em Vue:
<template>
<div>
<div>Price is ${{ price }}</div>
<div>Quantity is {{ quantity }}</div>
<div>Total is ${{ price * quantity}}</div>
<button @click="addQuantity">Add quantity</button>
</div>
</template>
<script>
export default {
data() {
return {
price: 10,
quantity: 0,
};
},
methods: {
addQuantity() {
this.quantity = this.quantity + 1;
},
},
};
</script>
O que está acontecendo é que:
- Temos duas variáveis, o
price
(sempre igual a 10) e aquantity
- Ao clicar no botão, uma função
addQuantity
é chamada para aumentar a variávelquantity
- O total é atualizado na página com
price * quantity
O
quantity
é mais do que apenas uma variável, é um estado. Os estados são como um sistema reativo pode realizar a renderização na página, pois é notificado quando um estado altera.Vamos dar uma olhada mais de perto na função
addQuantity
:addQuantity() {
this.quantity = this.quantity + 1;
}
Quando comecei com React, estava claro para mim que a função setState era responsável por notificar o motor do React para atualizar um estado no aplicativo (o nome da função já dá uma boa dica).
Mas no Vue, é uma atribuição. E se for JavaScript procedural sendo compilado (e é sim), então deve haver algo oculto que torne o código reativo (e novamente, existe sim).
Então, como o Vue sabe quando o estado de
quantity
mudou?Vamos começar com o Vue 2.
Reatividade em Vue 2
A reatividade dos estados no Vue 2 é baseada em Object.defineProperty, uma implementação JavaScript que pode criar um Design Pattern de Observer. Dê uma olhada neste exemplo:
const data = { quantity: 1 };
Object.defineProperty(data, 'quantity', {
get () {
console.log('get of quantity was called');
},
set () {
console.log('set of quantity was called');
}
})
data.quantity; // this will log 'get of quantity was called'
data.quantity = 2; // this will log 'set of quantity was called'
Esta implementação JavaScript cria funções getter e setter. Mas se quisermos mudar o estado de
data
, precisamos criar uma cópia dele (caso contrário, acessar ou atribuir um valor com essas funções acionaria um loop infinito).Usando o mesmo exemplo, o
dataQuantityCopy
armazena o valor:const data = { quantity: 1 };
let dataQuantityCopy = data.quantity;
Object.defineProperty(data, 'quantity', {
get () {
return dataQuantityCopy;
},
set (value) {
dataQuantityCopy = value
}
})
data.quantity;
data.quantity = 2;
Agora que temos algo próximo a um estado, podemos chamar outras funções ou qualquer reatividade necessária para atualizar algo ao usar
get
eset
de Object.defineProperty
.Uma implementação mais avançada poderia ser alcançada com
Object.keys(data).forEach
, então não apenas quantidade
funcionaria, mas quaisquer dados.Esta é uma simplificação do que está dentro do motor do Vue. Ao usar o Vue, os desenvolvedores não precisam se preocupar em como implementar o gerenciamento de estado em JavaScript.
Limitações no Vue 2 (armadilhas para estar ciente)
Essa solução funciona bem para valores primitivos, mas e se a propriedade também for um objeto ou um array? E se adicionar novos valores que não existiam antes nos
data
?Na estrutura do Vue, existem ressalvas e soluções alternativas para o tipo de objeto e array.
Objetos:
O Vue não pode detectar adição ou exclusão de propriedade. Os getters e setters são definidos apenas na inicialização do componente.
Uma solução alternativa para isso é usar Vue.set, que permite que o Vue saiba que há mudanças na estrutura do objeto e modifique os getters e setters de a propriedade.
Arrays:
O Vue não pode detectar mudanças por índice (exemplo:
item[index] = value
). O setter não é acionado.Uma solução alternativa para isso é usar Vue.set ou Array.splice. Ambos deixarão Vue saber que há mudanças.
Que entre o Vue 3
Uma das principais mudanças no motor do Vue relacionada aos estados é que
Object.defineProperty
foi substituído por Proxy e Reflect.Usando o mesmo exemplo de antes, o código ainda é semelhante:
const data = { quantity: 1 }
const proxiedData = new Proxy(data, {
get(target, key) {
console.log('get of ' + key + ' was called');
return Reflect.get(target, key)
},
set(target, key, value) {
console.log('set of ' + key + ' was called');
return Reflect.set(target, key, value)
}
})
proxiedData.quantity; // this will log 'get of quantity was called'
proxiedData.quantity = 2; // this will log 'set of quantity was called'
A API do
Proxy
funciona comoObject.defineProperty
. Mas a diferença é que o getter e o setter recebem a chave da propriedade que está sendo modificada, o que torna mais fácil inicializar todo o estado para ser reatividade (assim, novas adições de propriedades são acionadas, eliminando as ressalvas no Vue 2).A API do
Reflect
funciona de forma semelhante à avaliação ou atribuição, mas por serem funções, eles sabem como obter e definir os dados sem acionar o Proxy novamente, evitando assim loops infinitos.Limitações no Vue 3
Essas APIs estão disponíveis em ES6 e bibliotecas polyfill como Babel não suportam transpilações de
Proxy
.Isso significa que esses recursos não funcionam em navegadores antigos, então Vue 3 não é compatível com IE11 (veja mais informações nesta RFC).
Conclusão
O Vue 3 vem com melhorias significativas na reatividade em seu motor, sem afetar a API para desenvolvedores. Podemos continuar a usá-lo como antes e sem precisar nos preocupar com as ressalvas anteriores ao lidar com objetos e matrizes.
Essa mudança teve o custo de descartar o suporte para o IE11. Portanto, ao escolher bibliotecas e frameworks para construir um aplicativo, devemos considerar se o suporte a navegadores antigos como o IE11 é relevante, o que pode tornar o Vue 3 não ser a melhor opção.