4. Propriedades Computadas Puras
As propriedades computadas puras foram introduzidas no Knockout 3.2.0 e oferecem ganhos significativos de desempenho e otimização de memória em comparação com as computadas comuns. Isso ocorre porque elas não mantêm asisnaturas ativas quando não há subscribers. As principais vantagens são:
- Prevenção de vazamentos de memória – evitam que uma computação continue referenciando dependências mesmo após perder todas as referências na aplicação.
- Redução de custo computacional – a reavaliação só ocorre quando o valor é efetivamente observado por algum subscriber.
Uma propriedade computada pura alterna automaticamente entre dois estados, dependendo se possui ou não subscribers:
- Modo "dormindo" – quando não há subscribers, ela libera todas as assinaturas das dependências. Neste estado, ela não se inscreve nos observáveis acessados dentro da função evaluadora (embora continue rastreando‑os). Se o valor for lido durante o sono e alguma dependência tiver mudado, a reavaliação ocorre normalmente.
- Modo "acordado" – quando um subscriber é adicionado, ela se ativa e assina todas as dependências imediatamente. Neste estado, comporta‑se como uma computada comum.
O que é uma função "pura"?
O termo vem da programação funcional. Uma computada pura deve atender a dois critérios:
- Avaliar a função não deve causar efeitos colaterais.
- O valor da computada deve depender apenas de outros observáveis no sistema (seus "argumentos"), não de estado oculto ou do número de avaliações.
Sintaxe
A forma padrão de definir uma computada pura é usando ko.pureComputed:
this.nomeCompleto = ko.pureComputed(function() {
return this.primeiroNome() + " " + this.ultimoNome();
}, this);
Ou utilizando ko.computed com a opção pure:
this.nomeCompleto = ko.computed(function() {
return this.primeiroNome() + " " + this.ultimoNome();
}, this, { pure: true });
1. Quando usar propriedades computadas puras
Você pode aplicar pure em qualquer computada, mas os benefícios são mais evidentes em dois cenários:
- Em view models persistentes (que permanecem por toda a vida da aplicação), o ganho é computacional.
- Em view models temporários (usados em diálogos, abas, etc.), a economia de memória é significativa.
Exemplo – um formulário com etapas que só mostra o nome completo na última etapa:
View
<div class="log" data-bind="text: logComputado"></div>
<!--ko if: passo() == 0-->
<p>Nome: <input data-bind="textInput: primeiroNome" /></p>
<!--/ko-->
<!--ko if: passo() == 1-->
<p>Sobrenome: <input data-bind="textInput: ultimoNome" /></p>
<!--/ko-->
<!--ko if: passo() == 2-->
<div>Tratamento: <select data-bind="value: tratamento, options: ['Sr.', 'Sra.','Srta.','Dr.']"></select></div>
<h2>Olá, <span data-bind="text: nomeCompleto"> </span>!</h2>
<!--/ko-->
<p><button type="button" data-bind="click: proximo">Próximo</button></p>
ViewModel
function DadosApp() {
this.primeiroNome = ko.observable('João');
this.ultimoNome = ko.observable('Silva');
this.tratamento = ko.observable('Dr.');
this.logComputado = ko.observable('Log: ');
this.nomeCompleto = ko.pureComputed(function () {
var valor = this.tratamento() + " " + this.primeiroNome() + " " + this.ultimoNome();
// (efeito colateral intencional para demonstração)
this.logComputado(this.logComputado.peek() + valor + '; ');
return valor;
}, this);
this.passo = ko.observable(0);
this.proximo = function () {
this.passo(this.passo() === 2 ? 0 : this.passo() + 1);
};
}
ko.applyBindings(new DadosApp());
2. Quando não usar puras
Evite pure quando a função evaluadora produz efeitos colaterais necessários, como:
- Disparar uma chamada de rede sempre que as dependências mudam: ```
ko.computed(function () {
var dados = ko.toJS(this);
meuServico.atualizar(dados);
}, this);
- Atualizar o DOM diretamente em um binding customizado: ```
ko.computed({
read: function () {
element.title = ko.unwrap(acessoValor());
},
disposeWhenNodeIsRemoved: element
});
Nesses casos, a computada pode entrar em modo de repouso e não executar a ação necessária. Prefira ko.computed comum.
3. Verificando se uma propriedade é pura
KO oferece ko.isPureComputed para detectar programaticamente:
var resultado = {};
ko.utils.objectForEach(meuObjeto, function (nome, valor) {
if (!ko.isComputed(valor) || ko.isPureComputed(valor)) {
resultado[nome] = valor;
}
});
4. Notificações de mudança de estado
Quando uma computada pura muda para o modo acordado, ela dispara o evento awake (com o valor atual). Ao dormir, dispara asleep (com undefined). Isso pode ser usado para inicialização/limpeza:
this.minhaComputada = ko.pureComputed(function () {
// ...
}, this);
this.minhaComputada.subscribe(function () {
// fazer algo quando for vinculada
}, this, "awake");
this.minhaComputada.subscribe(function () {
// fazer algo quando for desvinculada
}, this, "asleep");
Nota: o evento awake também é disparado em computadas comuns criadas com deferEvaluation.