4 pontos por GN⁺ 2024-11-04 | 1 comentários | Compartilhar no WhatsApp
  • Cash é uma biblioteca alternativa ao jQuery, muito leve, que oferece sintaxe no estilo jQuery para manipular o DOM em navegadores modernos com suporte a IE11+
  • Aproveita recursos de navegadores modernos para reduzir a base de código e permitir o uso de familiares métodos encadeáveis com um tamanho de arquivo muito menor
  • Não tem como objetivo atingir 100% de equivalência funcional com o jQuery, mas cobre a maioria dos casos de uso do dia a dia, e a API implementada é em geral compatível com a do jQuery
  • Seu tamanho é de 6KB em minified & gzipped, 76,6% menor que os 24,4KB do jQuery Slim 3.4.1, e pode ser reduzido ainda mais com partial builds
  • Oferece suporte a base de código em TypeScript, tipos TypeScript gerados a partir do código, eventos com namespace e compilações parciais que permitem excluir métodos individuais

O problema que o Cash resolve

  • Cash é uma alternativa ao jQuery para navegadores modernos, oferecendo o seletor $() no estilo jQuery e métodos de coleção encadeáveis para manipulação do DOM
  • O suporte é voltado a navegadores IE11+
  • O objetivo não é implementar todos os recursos do jQuery exatamente como são, mas os recursos implementados pelo Cash foram projetados para serem em sua maioria compatíveis com a API do jQuery
  • Usuários que estejam migrando do jQuery podem consultar o guia de migração

Comparação de tamanho e recursos

  • Na comparação de tamanho de arquivo, o Cash é menor que o Zepto 1.2.0 e o jQuery Slim 3.4.1
    • Unminified: 36.5KB
    • Minified: 16KB
    • Minified & Gzipped: 6KB
  • O jQuery Slim 3.4.1 tem 24.4KB em minified & gzipped, e o Cash entrega uma redução de 76,6% em relação a ele
  • Se for necessário um bundle ainda menor, é possível usar partial builds
  • Em termos de recursos, o Cash oferece suporte a navegadores modernos, manutenção ativa, eventos com namespace, base de código em TypeScript e tipos TypeScript gerados a partir do código
  • Em Partial builds, o Cash permite excluir métodos individuais, enquanto Zepto e jQuery Slim são indicados como exclusão apenas por módulo completo

Como usar

  • O Cash pode ser carregado via jsDelivr e usado diretamente no navegador
<script src="https://cdn.jsdelivr.net/npm/cash-dom/…;
<script>
  $(function () {
    $('html').addClass ( 'dom-loaded' );
    $('<footer>Appended with Cash</footer>').appendTo ( document.body );
  });
</script>
  • O pacote npm é fornecido com o nome cash-dom
npm install --save cash-dom
import $ from "cash-dom";

$(function () {
  $('html').addClass ( 'dom-loaded' );
  $('<footer>Appended with Cash</footer>').appendTo ( document.body );
});

Estrutura da API

  • $() é o método seletor central do Cash e retorna uma coleção de nós manipulável
  • Se uma função for passada, essa função será executada quando o DOM estiver pronto
  • $() pode receber selector, DOM node, nodeList, HTML string, Cash collection e document ready callback
  • O Cash oferece, em linhas gerais, três tipos de API
    • Seletores de consulta
    • Métodos de coleção
    • Métodos da biblioteca no objeto global $

Métodos de coleção

  • Os métodos de coleção são chamados após criar uma coleção com $(), em um formato como $(element).addClass(className)
  • As categorias fornecidas se dividem em atributos, coleção, CSS, dados, dimensões, efeitos, eventos, formulários, manipulação de DOM, offset e navegação
  • Os principais métodos incluem
    • Classe/atributos: addClass, removeClass, toggleClass, attr, prop, removeAttr
    • Processamento de coleção: add, each, eq, filter, first, get, map, slice
    • Manipulação de DOM: append, prepend, before, after, html, text, clone, remove, replaceWith, wrap
    • Eventos: on, off, one, ready, trigger
    • Navegação: find, children, closest, parent, parents, siblings, next, prev
  • Alguns métodos extras são fornecidos, mas ficam desativados por padrão
  • $.fn é o protótipo principal da coleção e permite adicionar métodos personalizados a todas as coleções, como em plugins

Métodos globais do Cash

  • O objeto global $ inclui métodos de verificação de tipo e utilitários
  • Os métodos de verificação de tipo fornecem $.isArray, $.isFunction, $.isNumeric, $.isPlainObject, $.isWindow
  • Os utilitários incluem $.guid, $.each, $.extend, $.parseHTML, $.unique
  • $.extend expande o objeto de destino com propriedades dos objetos de origem e também oferece suporte a expansão profunda
  • $.parseHTML retorna uma coleção a partir de uma string HTML, e $.unique retorna um novo array com duplicatas removidas

Extensão e contribuição

  • O Cash pode ser estendido com métodos personalizados, e a forma de extensão está documentada em extending Cash
  • Problemas ou pedidos de recurso podem ser abertos como issue no GitHub
  • O fluxo de trabalho para pull requests é: clonar o repositório, instalar dependências, recompilar automaticamente com npm run dev, executar testes com npm run test e atualizar o README se necessário
  • A licença é MIT

1 comentários

 
GN⁺ 2024-11-04
Comentários no Hacker News
  • Hoje em dia os navegadores estão tão bons que, para simplificar manipulação de DOM, muitas vezes essas duas linhas de alias já bastam
    dqs = document.querySelector.bind(document);
    dqsA = document.querySelectorAll.bind(document);
    Assim, dá para usar dqs('#country') no lugar de document.querySelector('#country') e dqsA('.city') no lugar de document.querySelectorAll('.city')
    Para o resto, usar funções nativas do navegador costuma funcionar bem, e normalmente isso é importado de um módulo como import { dqs, dqsA } from '/lib/js/dqs.js';
    https://github.com/no-gravity/dqs.js

    • As duas linhas que fazem bind de querySelector/querySelectorAll parecem úteis e razoáveis, mas importar isso via import já é exagero
      São só duas linhas simples; não faz sentido virar dependência, é só copiar e colar
    • Nem toda função utilitária cabe em uma linha
      Por exemplo, para lidar com vários eventos, $.fn.one() e $.fn.on() são mais fáceis de usar com jQuery/Cash, e olhando a implementação interna dá para ver que há bastante trabalho envolvido: https://github.com/fabiospampinato/cash/blob/master/src/even...
    • querySelectorAll() não é uma coleção live, então muita gente faz logo a conversão para array assim
      dqsA = s => Array.from(document.querySelectorAll(s));
      Desse jeito, já dá para usar métodos de array como .map() e .filter() direto no resultado, ficando com uma sensação parecida com a do jQuery
    • Tentei recentemente remover uma dependência de React, mas em tratamento de eventos as diferenças entre navegadores ainda eram bem grandes
      Por exemplo, no Safari, durante os testes, o evento select de certo elemento simplesmente não disparava, e em alguns navegadores o evento não saía quando só o cursor era movido
      Mesmo sem precisar de recursos do React como componentes, estado e props, só as funções nativas do navegador não bastaram, e ficou claro o valor do React DOM em esconder essas diferenças entre navegadores
    • Parece parecido com o bling.js: https://gist.github.com/paulirish/12fb951a8b893a454b32
  • Depois de remover quase todos os polyfills, a vantagem duradoura que resta no jQuery, na minha opinião, é o processamento automático de listas
    A capacidade de desmarcar todos os botões selecionados em um formulário com uma única chamada ainda é difícil de igualar em outro lugar, e o mesmo vale para consultas a elementos-pai
    Mas o maior problema de implementação é que, quando a lista está vazia, ele falha silenciosamente
    Já corrigi bugs demais desse tipo, surgidos quando a árvore DOM foi refatorada depois por motivos de layout, então se eu refizesse o jQuery hoje, o padrão seria gerar erro em conjunto vazio, deixando a falha silenciosa só para quando você realmente não se importar, talvez com encadeamento ou alguma flag
    Há um tempo passei algumas horas olhando se daria para extrair o Sizzle e mudar isso nesse sentido, mas não levei adiante
    No fim, jQuery também se conecta à velha discussão de biblioteca versus framework, e depois de tanto tempo fazendo SPA com frameworks enormes, parece que estamos chegando de novo perto do vale da desilusão

    • Para muita gente, o apelo do jQuery está em não precisar se preocupar se um seletor corresponde de fato a algum elemento
      Se você manda esconder todos os .foo, eles são escondidos se existirem, e se não existirem nada acontece: uma abordagem fire and forget, parecida com CSS
      Se você escreve .foo { color: red; } e não existe .foo no documento, não há efeito colateral além de um pequeno overhead
    • Em navegadores modernos, modificar todos os itens do resultado de uma consulta também pode ser feito de forma bem limpa em uma linha
      document.querySelectorAll('input[type=checkbox]').forEach((i) => i.checked = false);
      Isso aproveita NodeList iterável e auxiliares de iteração
      Muitas consultas a elementos-pai podem ser resolvidas com element.closest()
    • Concordo totalmente que o padrão deveria ser estrito
      Código baseado em jQuery exige que todos os desenvolvedores conheçam todos os seletores do projeto e os atualizem quando o DOM muda, o que obviamente é impossível
      Parece viável trocar isso por uma implementação própria da função de inicialização do jQuery que obrigue a checar o comprimento
  • Num cenário em que sites populares despejam literalmente megabytes de JavaScript, não vejo por que reescrever uma biblioteca inteira, ainda por cima com menos recursos, só para economizar 50 KB

    • Independentemente deste pacote em si, sempre me espanto quando vejo esse tipo de lógica em discussões sobre jQuery
      Muitos textos criticam o uso de jQuery por causa do tamanho do pacote e de limitações de banda, enquanto ao mesmo tempo defendem frameworks SPA que consomem muito mais banda
      É um raciocínio de cargo cult completamente sem sentido
    • Essa lógica soa como “se redes de fast-food vendem refeições de 1600 calorias, por que fazer uma salada no almoço?” ou “se a dívida nacional está perto de 35 trilhões de dólares, por que comparar taxas de financiamento imobiliário?”
      Nos três casos, a resposta é a mesma: a coisa grande não sou eu; eu estou fazendo outra coisa, menor
      Outra resposta é que, se o problema é algo grande demais, então ficar menor parece justamente a solução
      No fim, a pergunta real parece ser por que um desenvolvedor gastaria tempo reescrevendo uma biblioteca, mas isso não é nada surpreendente
      Boa parte da programação é reescrever coisas que já existem, seja por necessidade de trabalho, por querer um comportamento ou perfil de desempenho um pouco diferente, ou simplesmente para aprender como aquilo funciona
    • Em vez de perguntar “por que se dar ao trabalho?”, aceitar dependências pequenas pode ajudar as dependências a ficarem menores também
    • Sites populares são quase lixo voltado à entrega de anúncios, e não deveriam servir de referência para o que devemos fazer
    • Ainda existem pessoas tentando manter todo o JavaScript abaixo de 50 KB
  • Se você estiver procurando uma alternativa ao jQuery, esperei tempo demais pelo jQuery 4.0 e acabei criando algo parecido com jQuery por conta própria, com algumas diferenças importantes
    Animações, tweens e timelines usam CSS puro em vez do sistema customizado do jQuery, lidam com elementos únicos e listas de forma transparente, e a proposta é favorecer o uso inline

    • A documentação entra em conflito
      Diz que me() retorna 1 elemento, ou o primeiro elemento, ou null, e que any() retorna um array ou um array vazio
      Mas os exemplos abaixo, como any('button')?.forEach(...) e any('button')?.map(...), sugerem que também pode ser null
      Fica confuso se any() sempre retorna um array, como a descrição acima diz, ou se null também é possível, como os exemplos abaixo sugerem
    • Gostei
      Tenho um interesse especial em localidade do comportamento
      Fico curioso sobre a experiência de usar currentScript.parentElement
      Quando pesquisei rapidamente no mês passado, fiquei com a impressão de que talvez não fosse confiável em casos de nicho, mas não me lembro exatamente quais eram
      Não fui a fundo, e fico feliz de ver isso funcionando bem
      Partindo do pressuposto de que não seja async nem module, me parece que currentScript.parentElement ainda deveria funcionar em todos os navegadores mesmo ao carregar 3 scripts seguidos
      O SvelteKit também discutiu isso e acabou implementando IDs aleatórios para apontar para o elemento de destino: https://github.com/sveltejs/kit/issues/2221
  • Ao olhar o guia de migração, aprendi algumas coisas que o jQuery consegue fazer e o Cash não, e que eu não conhecia mas talvez um dia viesse a usar
    https://github.com/fabiospampinato/cash/blob/master/docs/mig...

  • Como meta de expansão, seria legal usar a mágica de template strings do TypeScript para inferir com precisão o tipo do elemento
    Por exemplo, daria para inferir estaticamente que $('div#name') é um HTMLDivElement

    • Esse pacote se chama typed-query-selector
      Um exemplo de uso real está aqui: https://github.com/GoogleChrome/lighthouse/blob/main/types/i...
    • Elixir e algumas outras linguagens conseguem fazer esse tipo de coisa com pattern matching e sistemas de tipos, mas muitas linguagens não
      Não sei se isso é possível em TypeScript, e não consigo visualizar bem como seria feito
  • Ouvi dizer que o jQuery 4 é a alternativa ao jQuery para navegadores modernos

  • No começo usei isso na extensão de navegador que estou criando, mas no fim migrei para uma biblioteca JSX
    Depois que você sai da área de “app simples”, jQuery rapidamente vira um tipo de código difícil de raciocinar, e digo isso até como alguém que criou uma biblioteca inspirada em jQuery
    No fim, é preciso usar a ferramenta certa para o trabalho
    [1]: https://github.com/aleclarson/dough
    Se você consegue lidar bem com jQuery em apps médios ou grandes, tudo bem, mas não faz meu estilo

  • Antigamente, quando eu tentava reduzir o JS, usava https://github.com/filamentgroup/shoestring
    O principal motivo era que ele oferecia um build customizado com só o que eu realmente precisava
    O Cash parece ter algo parecido, mas está um pouco mais escondido na documentação: https://github.com/fabiospampinato/cash/blob/master/docs/par...
    Se eu fosse usar, provavelmente tentaria isso primeiro
    Ainda assim, continuo achando melhor simplesmente usar o que os navegadores oferecem hoje em dia
    Na prática isso já é bem bom, e jQuery não é mais realmente necessário
    Ainda mais quando até uma pequena alternativa ao jQuery já tem 6 kB, enquanto Preact, uma biblioteca estilo React, tem metade disso

  • Não tenho certeza se isso ajuda em algo além de criar apelidos para Web APIs que já existem