33
loading...
This website collects cookies to deliver better user experience
this.
, como o exemplo dessa interpolação de string:export default {
data() {
return {
nome: 'John',
sobrenome: 'Doe'
}
},
computed: {
nomeCompleto() {
return `${this.nome} ${this.sobrenome}`
}
}
}
// this.fullName -> John Doe
Esse texto é baseado na documentação do Vue 3 e especialmente no texto Reactivity in Depth, que recomendo fortemente a sua leitura.
nomeCompleto
for a concatenação de nome
e sobrenome
, o seu resultado deve ser atualizado sempre que um desses dois valores mudar.let nome = 'John'
let sobrenome = 'Doe'
let nomeCompleto = nome + ' ' + sobrenome
console.log(nomeCompleto) // -> John Doe
nome = 'Caio'
sobrenome = 'Ferrarezi'
console.log(nomeCompleto) // -> John Doe
nomeCompleto
não é atualizado porque não dissemos ao Javascript para fazê-lo. Isso é o que é chamado de programação imperativa, devemos dar as instruções precisas para a máquina fazer o que a gente quer.string
, number
, boolean
e etc), devemos usar estruturas como os objetos.Object
são passados a frente, são suas referências que são mandadas e não suas cópias, ou seja, se alterarmos seus valores, isso acontecerá diretamente no espaço de memória onde estão alocados.estado
para o nosso sistema, bastando instanciar um novo objeto proxy:const estado = new Proxy({
nome: 'John',
sobrenome: 'Doe'
})
const manipuladores = {
get(objeto, chave) {
return objeto[chave]
},
set(objeto, chave, valor) {
objeto[chave] = valor
}
}
const estado = new Proxy({
nome: 'John',
sobrenome: 'Doe'
}, manipuladores)
estado
, essas operações serão feitas normalmente:console.log(estado.nome) // -> John
estado.nome = 'Caio'
console.log(estado.nome) // -> Caio
estado
, vamos criar a função rastreia
que vai guardar dentro de um WeakMap
a referência ao objeto original que passamos na proxy e, futuramente, suas dependências. A implementação é bem simples, começando com a instância de uma nova estrutura de dados.const mapaDeObjetos = new WeakMap()
WeakMap
. Essa estrutura de dados (que também foi introduzida com o ES6) permite que guardemos um par chave-valor com diferencial de que essa chave pode ser um objeto (no nosso caso o objeto original do estado
), função ou outra estrutura mais complexa.rastreia
. Inicialmente, vamos procurar pelo valor referente ao objeto no mapaDeObjetos
e se ele não existir, criá-lo:function rastreia(objeto, chave) {
let mapaDeDependencias = mapaDeObjetos.get(objeto)
if (!mapaDeDependencias) {
mapaDeDependencias = new Map()
mapaDeObjetos.set(objeto, mapaDeDependencias)
}
}
mapaDeDependencia
que foi criado, é onde vamos guardar futuramente as dependências das propriedades do estado
, mas falaremos delas mais profundamente daqui a pouco. O que precisamos saber agora é que esse mapa vai guardar uma coleção de dados que não podem ser iguais entre si, por isso vamos usar a estrutura de dados Set
:function rastreia(objeto, chave) {
// ...
let dependencias = mapaDeDependencias.get(chave)
if (!dependencias) {
dependencias = new Set()
mapaDeDependencias.set(chave, dependencias)
}
}
estado
.efeito
) que lê valores da proxy.efeito
globalmente:let efeitoAtivo = null
criaEfeito
é quem vai manipular a variável que acabamos de criar. Ela vai receber um efeito, expor e executar esse código:function criaEfeito(efeito) {
efeitoAtivo = efeito
efeito()
efeitoAtivo = null
}
estado.nome
e estado.sobrenome
:criaEfeito(() => console.log(`${estado.nome} ${estado.sobrenome}`))
rastreia
para que ao fim ela adicione o efeito ativo ao Set
de dependências:function rastreia(objeto, chave) {
// ...
if (efeitoAtivo) {
dependencias.add(efeitoAtivo)
}
}
get
para chamar a função rastreia
:const manipuladores = {
get(objeto, chave) {
rastreia(objeto, chave)
return objeto[chave]
},
// ...
}
estado
(proxy) com as propriedades nome
e sobrenome
;estado.nome
e estado.sobrenome
;get
de cada uma; eget
chama a função rastreia
que guarda o efeito ativo em uma coleção atrelada à propriedade lida.estado
é muito fácil, na verdade, já estamos fazendo isso. Essa responsabilidade está com o manipulador set
da proxy. Toda vez que alteramos o valor tanto de estado.nome
, quanto de estado.sobrenome
, esse manipulador é chamado e a propriedade é atualizada.set
para chamar a função executa
logo depois de atribuir uma novo valor:const manipuladores = {
// ...
set(objeto, chave, valor) {
objeto[chave] = valor
executa(objeto, chave)
}
}
function executa(objeto, chave) {
const mapaDeDependencias = mapaDeObjetos.get(objeto)
if (mapaDeDependencias) {
const dependencias = mapaDeDependencias.get(chave)
dependencias.forEach(efeito => efeito())
}
}
executa
. É possível verificar se o valor antigo e o valor atual da propriedade são iguais e então ignorar a execução das dependências, já que na prática, ainda que o manipulador tenha sido chamado, os valores não foram alterados:const manipuladores = {
// ...
set(objeto, chave, valor) {
const valorAntigo = objeto[chave]
objeto[chave] = valor
if (valorAntigo !== valor) {
executa(objeto, chave)
}
}
}
estado.nome
ou estado.sobrenome
forem alterados, o log da concatenação desses valores será executado automagicamente:estado.nome = "Caio" // -> Caio Doe
estado.sobrenome = "Ferrarezi" // -> Caio Ferrarezi
estado.nome = "Caio" // Não executa a dependência!
let efeitoAtivo = null
const mapaDeObjetos = new WeakMap()
function criaEfeito(efeito) {
efeitoAtivo = efeito
efeito()
efeitoAtivo = null
}
function rastreia(objeto, chave) {
let mapaDeDependencias = mapaDeObjetos.get(objeto)
if (!mapaDeDependencias) {
mapaDeDependencias = new Map()
mapaDeObjetos.set(objeto, mapaDeDependencias)
}
let dependencias = mapaDeDependencias.get(chave)
if (!dependencias) {
dependencias = new Set()
mapaDeDependencias.set(chave, dependencias)
}
if (efeitoAtivo) {
dependencias.add(efeitoAtivo)
}
}
function executa(objeto, chave) {
const mapaDeDependencias = mapaDeObjetos.get(objeto)
if (mapaDeDependencias) {
const dependencias = mapaDeDependencias.get(chave)
dependencias.forEach(efeito => efeito())
}
}
const manipuladores = {
get(objeto, chave) {
rastreia(objeto, chave)
return objeto[chave]
},
set(objeto, chave, valor) {
const valorAntigo = objeto[chave]
objeto[chave] = valor
if (valorAntigo !== valor) {
executa(objeto, chave)
}
}
}
const estado = new Proxy({
nome: 'John',
sobrenome: 'Doe'
}, manipuladores)
criaEfeito(() => console.log(`${estado.nome} ${estado.sobrenome}`))
estado.nome = "Caio"
estado.sobrenome = "Ferrarezi"
estado.nome = "Caio"