Estado e cache
Os três lugares onde um valor pode morar numa aplicação Vitrine, e quem enxerga cada um.
O programa roda inteiro a cada interação. Sem um lugar que sobreviva, um contador voltaria a zero a cada clique. Há três, e a diferença entre eles é quem enxerga:
| Onde | Quem vê | Some quando |
|---|---|---|
V.estado | uma sessão | a sessão expira |
V.geral | todas as sessões | o processo termina |
V.cache | todas, por argumento | o TTL vence ou é invalidado |
Estado da sessão#
V.estado.padrao("contador", 0) // define só se ainda não existe
given V.botao("Incrementar"):
V.estado.somar("contador")
V.texto($"Valor: {V.estado.obter("contador")}")V.estado.padrao substitui as três linhas que todo app escreve no começo. V.estado.somar é atômico: duas abas clicando ao mesmo tempo não perdem uma das somas, o que o ler-somar-escrever à mão perderia.
| Chamada | Faz |
|---|---|
V.estado.obter(chave, padrão) | lê |
V.estado.definir(chave, valor) | escreve |
V.estado.padrao(chave, valor) | escreve só se não existe; devolve o que vale |
V.estado.somar(chave, quanto) | incrementa, sem corrida |
V.estado.existe(chave) | yes/no |
V.estado.remover(chave) | apaga uma |
V.estado.limpar() | apaga todas |
V.estado.tudo() | o vault, sem as chaves internas |
V.estado.id() | o identificador da sessão |
Estado global#
V.geral é compartilhado por todas as sessões — configuração, um contador de visitas, um modelo de ML carregado uma vez.
V.geral.definir("versao", "1.0.0")
V.geral.somar("visitas")Cache#
mark @V.cache sobre uma ação, e ela para de recalcular. Vale por argumento: vendas("2026-01") e vendas("2026-02") ocupam entradas diferentes.
mark @V.cache
action vendas(mes):
yield Banco.consultar("SELECT … WHERE mes = ?", [mes])Com ajustes:
mark @V.cache(validade := 300, teto := 32)
action cotacao(moeda):
yield Http.get($"https://…/{moeda}").json()
mark @V.cache(pasta := ".cache/ibge")
action municipios():
yield Http.get("https://…/municipios").json()| Opção | Faz |
|---|---|
validade | segundos até o valor vencer (TTL) |
teto | quantos valores guardar; ao encher, sai o menos usado (LRU) |
pasta | também grava em disco, e sobrevive a reiniciar |
O teto existe porque um cache sem limite é um vazamento com outro nome: uma ação chamada com mil argumentos diferentes guardaria mil resultados e não soltaria nenhum.
Esvaziar#
V.cache.invalidar(vendas) // só essa ação
V.cache.invalidar() // tudo
V.cache.estatisticas() // acertos, erros e taxa, por açãoE vendas.sem_cache(mes) chama a ação original — é o que permite testá-la sem o cache no caminho.
Identidade do componente#
O valor de um campo sobrevive a um clique em outro lugar da página porque cada componente tem uma chave. Sem chave, ela sai do tipo, do rótulo e da posição — estável enquanto o programa não muda.
// Dê uma chave quando a ordem dos componentes pode mudar:
cycle cliente in clientes:
V.entrada("Observação", chave := $"obs-{cliente["id"]}")Sem a chave explícita aqui, remover um cliente da lista faria as observações dos seguintes escorregarem uma posição.