Propriedades Computadas Puras no Knockout.js

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:

  1. 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.
  2. 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:

  1. Avaliar a função não deve causar efeitos colaterais.
  2. 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.

Tags: Knockout.js Propriedades Computadas Puras ko.pureComputed Otimização de Memória javascript

Publicado em 8-8 14:22