- 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
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 dedocument.querySelector('#country')edqsA('.city')no lugar dedocument.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
querySelector/querySelectorAllparecem úteis e razoáveis, mas importar isso viaimportjá é exageroSão só duas linhas simples; não faz sentido virar dependência, é só copiar e colar
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 assimdqsA = 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 jQueryPor exemplo, no Safari, durante os testes, o evento
selectde certo elemento simplesmente não disparava, e em alguns navegadores o evento não saía quando só o cursor era movidoMesmo 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
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
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 CSSSe você escreve
.foo { color: red; }e não existe.foono documento, não há efeito colateral além de um pequeno overheaddocument.querySelectorAll('input[type=checkbox]').forEach((i) => i.checked = false);Isso aproveita
NodeListiterável e auxiliares de iteraçãoMuitas consultas a elementos-pai podem ser resolvidas com
element.closest()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
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
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
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
Diz que
me()retorna 1 elemento, ou o primeiro elemento, ounull, e queany()retorna um array ou um array vazioMas os exemplos abaixo, como
any('button')?.forEach(...)eany('button')?.map(...), sugerem que também pode sernullFica confuso se
any()sempre retorna um array, como a descrição acima diz, ou senulltambém é possível, como os exemplos abaixo sugeremTenho um interesse especial em localidade do comportamento
Fico curioso sobre a experiência de usar
currentScript.parentElementQuando 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
asyncnemmodule, me parece quecurrentScript.parentElementainda deveria funcionar em todos os navegadores mesmo ao carregar 3 scripts seguidosO 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')é umHTMLDivElementtyped-query-selectorUm exemplo de uso real está aqui: https://github.com/GoogleChrome/lighthouse/blob/main/types/i...
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