Executando verificação de segurança...
6
exs
5 min de leitura ·

Pitch: No.JS: um framework reativo HTML-first (sem necessidade de JS da sua parte)

Estive trabalhando em um projeto paralelo nos últimos 5 meses, e finalmente cheguei a um ponto onde me sinto confortável em compartilhá-lo.

Ele se chama No.JS. É um framework reativo HTML-first. A ideia é simples: e se você pudesse construir web apps reativos usando apenas atributos HTML, sem escrever JavaScript?


Como tudo começou

Eu estava no meu último emprego, imerso em uma base de código Angular. Não era ruim, honestamente. Bem arquitetada, boa equipe. Mas um dia eu precisei adicionar um dropdown que filtrava uma tabela. Coisa simples, o tipo de coisa que deveria levar dez minutos, no máximo.

Eu criei um componente, um módulo para declará-lo, um service para buscar os dados, uma interface para o tipo de resposta, um pipe observável para fazer o debounce do input, e um template que referenciava tudo isso. Seis arquivos, talvez umas quarenta linhas espalhadas entre eles, apenas para dizer "quando isso mudar, busque aquilo de novo e mostre aqui."

Lembro de olhar pro meu vscode, clicando entre filter-dropdown.component.ts, filter-dropdown.module.ts, filter.service.ts, filter.model.ts, e pensando: a lógica real com a qual eu me importo cabe em uma frase. Todo o resto é apenas o framework e as convenções estabelecidas (ODEIO ELAS!) me pedindo para provar que estou falando sério ou que eu sei toda aquela ˆ%&ˆ*(.

Esse pensamento ficou na minha cabeça. Então fui dar uma olhada por aí. Encontrei um projeto incrível com um nome ainda melhor: HTMX.

HTMX foi a primeira coisa que tentei. Um projeto genuinamente excelente, e ele acerta em cheio no modelo server-driven. Se o seu backend é o cérebro, o HTMX simplesmente conecta o HTML a ele de forma linda. Mas eu não tinha um backend. Eu tinha uma página estática e uma API pública. O HTMX assume um servidor que retorna fragmentos de HTML, e para o meu caso de uso isso significava que eu ainda precisaria subir um servidor só para fazer proxy e criar templates das respostas.

Aí eu tentei o Alpine.js. Mais próximo do que eu queria. Reativo, leve, fica no HTML. Gostei muito. Mas depois de alguns dias, continuei esbarrando em paredes: sem HTTP declarativo, sem roteamento SPA, sem um padrão nativo de loops sobre dados buscados. Eu estava escrevendo pequenos scripts x-init para buscar, fazer o parse e atribuir dados, para então conectar o x-for separadamente. Funcionava, mas parecia que eu mesmo estava montando o encanamento todas as vezes, e o que eu queria (apenas apontar este elemento para um endpoint e renderizar o que voltasse) estava sempre fora de alcance.

O que me faltava era o meio-termo. Algo que viva inteiramente no HTML como o Alpine, converse com APIs como o HTMX, mas trate todo o ciclo de vida (fetch, bind, loop, route) como uma superfície contínua. Não é uma história de servidor. Não é uma história de scripting. É uma história de HTML.

Então comecei a construir um.


Como ele é

Uma caixa de pesquisa reativa no No.JS:

<div state="{ query: '' }" get="/api/search?q={{ query }}" as="results">
  <input model="query" />
  <li each="r in results" bind="r.name"></li>
</div>

Quatro linhas. É reativo, faz o fetch automaticamente quando a query muda, e renderiza os resultados. Sem imports, sem hooks, sem etapa de build. (Peguei isso da minha documentação html só pra mostrar a vocês como funciona)


O pensamento por trás disso

Os navegadores já entendem HTML. Eles já lidam com eventos, atualizam o DOM, gerenciam o layout. Em algum momento no meio do caminho, começamos a tratar o navegador como algo a ser contornado, em vez de algo com o qual trabalhar.

O HTMX provou que muitas pessoas sentem a mesma atração de volta para o HTML. O Alpine provou que você pode ter reatividade sem uma etapa de build. O No.JS tenta levar isso mais longe: e se os atributos HTML pudessem cobrir toda a superfície (busca de dados, estado, roteamento, validação, i18n) para que você nunca mais precise descer para um bloco de script?

Os atributos se tornam a API: bind para dados, each para loops, get para buscas, state para reatividade. Seus templates são HTML válido que qualquer navegador consegue ler.

Não é anti-JavaScript. Ainda tem JS rodando por baixo dos panos. Mas a camada voltada para o desenvolvedor é o HTML, e para muitos casos de uso, isso acaba sendo o suficiente.


O que está incluso

É mais completo do que você esperaria:

  • HTTP declarativo (get, post, put, delete)
  • Binding reativo (bind, model)
  • Condicionais e loops (if, show, each, switch)
  • Gerenciamento de estado (state local, store global, computed, watch)
  • Roteamento SPA com guards, parâmetros e rotas aninhadas
  • Validação de formulários
  • Animações e transições
  • i18n (internacionalização) com pluralização
  • Mais de 30 filtros nativos
  • Diretivas customizadas

~11 KB gzipped, zero dependências.


Em que pé está

Eu reescrevi o núcleo três vezes. Eu fui e voltei na API de diretivas mais do que gostaria de admitir. Escrevi testes, escrevi a documentação e construí o site da documentação com o próprio No.JS.

Ele não vai substituir o React para projetos de grandes equipes com necessidades de ferramentas complexas. Esse não é o objetivo. Mas para landing pages, dashboards, ferramentas internas, protótipos ou qualquer coisa em que você precise apenas de algo reativo sem tanta cerimônia, ele funciona muito bem.

Uma coisa sobre a qual serei honesto

Quando a sua linguagem de template vive em atributos HTML e avalia expressões em tempo de execução, você está essencialmente entregando ao navegador um minúsculo interpretador. Isso me tira um pouco o sono. Coloquei algumas proteções em prática (avaliação em sandbox, nada de construtor Function em inputs voltados pro usuário, isolamento de escopo entre componentes), mas eu não testei isso em batalha do jeito que um framework com cinco anos e cem contribuidores testou. Superfícies de XSS, injeção de expressão, o que acontece quando alguém joga dados de API não sanitizados direto em um bind – ainda estou mapeando tudo isso.

Se você já trabalhou com políticas CSP, sanitização de templates ou sandboxing em tempo de execução e algo aqui te faz estremecer, eu genuinamente quero te ouvir. Segurança é a única área onde "funciona na minha máquina" não é bom o suficiente, e prefiro que alguém encontre as falhas agora do que descobrir da pior maneira mais tarde.

O projeto é open source (MIT): github.com/ErickXavier/no-js

Se quiser experimentar:

<script src="[https://unpkg.com/@erickxavier/no-js@latest/dist/iife/no.js](https://unpkg.com/@erickxavier/no-js@latest/dist/iife/no.js)"></script>

Esse é o setup inteiro.

A propósito, eu não queria meu nome na url do pacote npm, mas apenas no-js era parecido demais com outros 2 projetos mortos: nojs e no.js. E eu apenas segui a sugestão do NPMJS, usei meu nome (username do github).

Eu cobri o projeto com testes, mas espero que a comunidade encontre bugs e crie seus próprios PRs. Por favor, façam isso! Preciso de toda a ajuda possível com este aqui!


No geral, estou curioso para saber o que as pessoas acham. Estive focado nisso por um bom tempo e adoraria uma perspectiva externa. Feedback, perguntas, críticas, sugestões, são todos bem-vindos.

Carregando publicação patrocinada...
1

Uma das coisas mais interessantes que ja vi aqui no tabnews, eu sei bem como é ter que seguir regras desnecessária, apesar de não ser dev frontend, sou dev backend, uso muito o Nestjs, e particularmente, isso que você desenvolveu é sensacional no meu ponto de vista, trás simplicidade para o workflow do programador, parabens pela ideia

1

Olá Erick, pessoalmente eu gostei quê você não tentou recriar o Htmx, um amigo teve a mesma ideia que a sua há um tempo atrás, porém, ele caiu no bait de recriar o htmx sem diferenças reais.

Por agora o quê me chama atenção com certeza é o distanciamento do backend e "preferência à visão do dev" com foco em cobrir as vulnerabilities do front (ao menos onde dá). Existem N motivos pelo qual um dev optaria por uma solução como a sua, e N motivos pelo qual evitaria.

Para um feedback mais preciso eu precisaria utilizar seu projeto em meu ambiente de Prod Test. O que farei no próximo mês, talvez seja exatamente o quê eu andava buscando.

1

Valeu demais, Jhon! Fico feliz que a abordagem tenha feito sentido para você. Se precisar de alguma ajuda na hora de rodar os testes no mês que vem, estou por aqui (e via github tbm).

Um rápido update:

Estou trabalhando em testes e2e, usando Playwright, com htmls contendo exemplos pra todas as funcionalidades.

Por conta dos que eu já escrevi, alguns bugs foram encontrados e corrigidos.

Já enviei a primeira leva de correções e testes.

Segue commit: https://github.com/ErickXavier/no-js/commit/12fec61d0d8ecaf4d61cf97822fc3043df24bb47