4 puntos por GN⁺ 2024-11-04 | 1 comentarios | Compartir por WhatsApp
  • Cash es una biblioteca alternativa a jQuery muy pequeña que ofrece una sintaxis estilo jQuery para manipular el DOM en navegadores modernos con soporte para IE11+
  • Aprovecha funciones de los navegadores modernos para reducir la base de código y permitir usar los conocidos métodos encadenables con un tamaño de archivo mucho menor
  • No busca una equivalencia funcional del 100% con jQuery, pero cubre la mayoría de los casos de uso cotidianos y la API implementada es en gran medida compatible con jQuery
  • Su tamaño es de 6 KB minificado y comprimido con gzip, un 76.6% más pequeño que los 24.4 KB de jQuery Slim 3.4.1, y puede reducirse aún más con compilaciones parciales
  • Ofrece una base de código en TypeScript, tipos de TypeScript generados desde el código, eventos con espacio de nombres y soporte para compilaciones parciales que permiten excluir métodos individuales

El problema que resuelve Cash

  • Cash es una alternativa a jQuery para navegadores modernos que proporciona el selector $() al estilo jQuery y métodos de colección encadenables para manipular el DOM
  • El soporte está dirigido a navegadores IE11+
  • No tiene como objetivo implementar todas las funciones de jQuery tal cual, pero las funciones que Cash sí implementa están diseñadas para ser en su mayoría compatibles con la API de jQuery
  • Los usuarios que migren desde jQuery pueden consultar la migration guide

Comparación de tamaño y funciones

  • En la comparación de tamaño de archivo, Cash es más pequeño que Zepto 1.2.0 y jQuery Slim 3.4.1
    • Sin minificar: 36.5KB
    • Minificado: 16KB
    • Minificado y con gzip: 6KB
  • jQuery Slim 3.4.1 pesa 24.4KB minificado y con gzip, y Cash representa una reducción de tamaño del 76.6% frente a este
  • Si se necesita un paquete aún más pequeño, se pueden usar partial builds
  • En comparación de funciones, Cash ofrece soporte para navegadores modernos, mantenimiento activo, eventos con espacio de nombres, base de código en TypeScript y tipos de TypeScript generados desde el código
  • En Cash, las compilaciones parciales permiten excluir métodos individuales, mientras que en Zepto y jQuery Slim se indica exclusión por módulos completos

Cómo usarlo

  • Cash puede cargarse desde jsDelivr y usarse directamente en el 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>
  • El paquete de npm se ofrece con el nombre cash-dom
npm install --save cash-dom
import $ from "cash-dom";

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

Estructura de la API

  • $() es el método selector central de Cash y devuelve una colección de nodos manipulables
  • Si se le pasa una función, ejecuta esa función cuando el DOM está listo
  • $() puede recibir un selector, un nodo del DOM, una nodeList, una cadena HTML, una colección de Cash o un callback de document ready
  • Cash ofrece en términos generales tres tipos de API
    • Selectores de consulta
    • Métodos de colección
    • Métodos de biblioteca del objeto global $

Métodos de colección

  • Los métodos de colección se llaman después de crear una colección con $(), de una forma como $(element).addClass(className)
  • Las categorías disponibles se dividen en atributos, colección, CSS, datos, dimensiones, efectos, eventos, formularios, manipulación del DOM, offset y recorrido
  • Entre los métodos principales se incluyen los siguientes
    • Clase/atributo: addClass, removeClass, toggleClass, attr, prop, removeAttr
    • Manejo de colecciones: add, each, eq, filter, first, get, map, slice
    • Manipulación del DOM: append, prepend, before, after, html, text, clone, remove, replaceWith, wrap
    • Eventos: on, off, one, ready, trigger
    • Recorrido: find, children, closest, parent, parents, siblings, next, prev
  • Se ofrecen algunos extra methods, pero están desactivados por defecto
  • $.fn es el prototipo principal de la colección, y permite agregar métodos personalizados a todas las colecciones al estilo de un plugin

Métodos globales de Cash

  • El objeto global $ incluye métodos de verificación de tipos y utilidades
  • Los métodos de verificación de tipos ofrecen $.isArray, $.isFunction, $.isNumeric, $.isPlainObject, $.isWindow
  • Entre las utilidades se incluyen $.guid, $.each, $.extend, $.parseHTML, $.unique
  • $.extend amplía el objeto de destino con propiedades del objeto fuente, y también soporta extensión profunda
  • $.parseHTML devuelve una colección a partir de una cadena HTML, y $.unique devuelve un nuevo arreglo sin duplicados

Extensión y contribución

  • Cash puede ampliarse con métodos personalizados, y la forma de hacerlo está resumida en extending Cash
  • Los problemas o solicitudes de funciones pueden abrirse como issue en GitHub
  • El flujo de trabajo para pull requests consiste en clonar el repositorio, instalar dependencias, recompilar automáticamente con npm run dev, ejecutar pruebas con npm run test y actualizar el README si hace falta
  • La licencia es MIT

1 comentarios

 
GN⁺ 2024-11-04
Opiniones de Hacker News
  • Como los navegadores de hoy han mejorado, para simplificar la manipulación del DOM muchas veces bastan estos alias de dos líneas
    dqs = document.querySelector.bind(document);
    dqsA = document.querySelectorAll.bind(document);
    Así se puede usar dqs('#country') en lugar de document.querySelector('#country'), o dqsA('.city') en lugar de document.querySelectorAll('.city')
    Para lo demás está bien usar simplemente funciones nativas del navegador, y normalmente se importan desde un módulo, como import { dqs, dqsA } from '/lib/js/dqs.js';
    https://github.com/no-gravity/dqs.js

    • Las dos líneas que hacen bind de querySelector/querySelectorAll en sí parecen útiles y razonables, pero traerlas con import es excesivo
      Son apenas dos líneas simples, no hay razón para ponerlas como dependencia; basta con copiarlas y pegarlas
    • No todas las funciones utilitarias son de una sola línea
      Por ejemplo, para manejar varios eventos, $.fn.one() o $.fn.on() son más fáciles de usar con jQuery/Cash, y si se mira la implementación interna hay bastante trabajo: https://github.com/fabiospampinato/cash/blob/master/src/even...
    • querySelectorAll() no es una colección live, así que muchas veces se convierte directamente en arreglo así
      dqsA = s => Array.from(document.querySelectorAll(s));
      De esta forma se pueden usar de inmediato métodos de arreglo como .map() o .filter() sobre el resultado, lo que mantiene una sensación parecida a jQuery
    • Hace poco intenté eliminar la dependencia de React, pero en el manejo de eventos las diferencias entre navegadores seguían siendo bastante grandes
      Por ejemplo, durante las pruebas en Safari el evento select de cierto elemento directamente no se disparaba, y en algunos navegadores no había evento cuando solo se movía el caret
      Aunque no necesitara funciones de React como componentes, estado o props, las funciones nativas del navegador no bastaban, y quedó claro el valor de React DOM para cubrir las diferencias entre navegadores
    • Se parece a bling.js: https://gist.github.com/paulirish/12fb951a8b893a454b32
  • Después de quitar casi todos los polyfills, creo que la ventaja duradera que le queda a jQuery es el procesamiento automático de listas
    La capacidad de desmarcar con una sola llamada todos los botones dentro de un formulario todavía es difícil de igualar en otros lados, y lo mismo pasa con las consultas al padre
    Pero el mayor problema de la implementación es que, cuando la lista está vacía, falla silenciosamente
    Como ya corregí demasiados bugs de este tipo al refactorizar después el árbol DOM con fines de layout, si hoy volviera a hacer jQuery, por defecto lanzaría un error con conjuntos vacíos, y dejaría una llamada encadenada o un flag para fallar silenciosamente solo cuando realmente no importe
    Hace tiempo pasé unas horas viendo si se podía sacar Sizzle y cambiarlo de esa forma, pero no seguí avanzando
    Al final, jQuery también se conecta con el viejo debate de librería vs. framework, y después de pasar tanto tiempo haciendo apps de una sola página con frameworks enormes, siento que se acerca de nuevo el valle de la desilusión

    • Para mucha gente, el atractivo de jQuery es no tener que preocuparse por si un selector coincide con elementos reales
      Si le ordenas ocultar todos los .foo, si existen los oculta y si no, no pasa nada: un enfoque fire and forget, parecido a CSS
      Si escribes .foo { color: red; } y no hay .foo en el documento, aparte de un pequeño overhead no hay efectos secundarios
    • En navegadores modernos, modificar todos los elementos de un resultado de consulta también se puede hacer de forma bastante limpia en una línea
      document.querySelectorAll('input[type=checkbox]').forEach((i) => i.checked = false);
      Es una forma de aprovechar NodeList iterable y helpers de iteradores
      Muchas consultas al padre pueden resolverse con element.closest()
    • Estoy totalmente de acuerdo en que el valor por defecto debería ser estricto
      El código basado en jQuery exige que todos los desarrolladores conozcan todos los selectores del proyecto y los actualicen cuando cambie el DOM, algo que obviamente es imposible
      Parece viable reemplazar la función de inicialización de jQuery por una implementación propia que fuerce la comprobación de longitud
  • En un contexto donde los sitios web mainstream reparten literalmente megabytes de JavaScript, no entiendo por qué habría que reescribir una librería completa con menos funciones solo para ahorrar 50 KB

    • Más allá de este paquete en particular, me desconcierta cada vez que veo este razonamiento en discusiones sobre jQuery
      Muchos artículos se oponen al uso de jQuery por el tamaño del paquete y las restricciones de ancho de banda, mientras al mismo tiempo defienden frameworks SPA que consumen muchísimo más ancho de banda
      Es un razonamiento de culto cargo completamente absurdo
    • Ese argumento suena parecido a “las cadenas mainstream de comida rápida venden comidas de 1600 calorías, ¿por qué prepararte una ensalada para el almuerzo?” o “la deuda nacional se acerca a los 35 billones de dólares, ¿por qué comparar tasas hipotecarias?”
      En los tres casos, la respuesta es la misma: lo grande no soy yo, y yo estoy haciendo otra cosa más pequeña
      Otra respuesta es que, si algo demasiado grande es el problema, hacerse más pequeño suena como una solución
      Al final, parece que lo que se quiere preguntar es por qué un desarrollador dedicaría tiempo a reescribir una librería, pero eso no es tan sorprendente
      Una parte considerable de la programación consiste en reescribir cosas ya hechas, ya sea porque el trabajo lo requiere, porque se necesita un comportamiento o características de rendimiento un poco distintas, o simplemente porque se quiere aprender cómo funcionan
    • En vez de decir “¿para qué molestarse?”, si se aceptan dependencias pequeñas, las dependencias también pueden volverse más pequeñas
    • Los sitios web mainstream se parecen más a basura para entregar anuncios, y no deberíamos tomarlos como referencia de lo que tenemos que hacer
    • Todavía hay gente que intenta enviar todo el JavaScript en menos de 50 KB
  • Si estás buscando una alternativa a jQuery, después de esperar demasiado por jQuery 4.0 terminé creando algo propio parecido a jQuery, con algunas diferencias importantes.
    Para animaciones, tweens y timelines usa CSS puro en vez del sistema personalizado de jQuery; maneja de forma transparente elementos individuales y listas, y apunta más a usarse inline.

    • La documentación se contradice.
      Dice que me() devuelve 1 elemento, o el primer elemento, o null, y que any() devuelve un arreglo o un arreglo vacío.
      Pero los ejemplos de abajo, como any('button')?.forEach(...) y any('button')?.map(...), implican que también podría ser null.
      Me confunde si any() siempre devuelve un arreglo como dice la descripción anterior, o si también puede ser null como en los ejemplos de abajo.
    • Está bueno.
      Me interesa especialmente la localidad del comportamiento.
      Tengo curiosidad por saber qué experiencia han tenido usando currentScript.parentElement.
      Cuando lo investigué rápidamente el mes pasado, me quedó la impresión de que quizá no era confiable en casos borde, aunque no recuerdo exactamente cuándo.
      No lo investigué a fondo, y me alegra que hayan logrado que funcione bien.
      Si asumimos que no es async ni module, me parece que incluso cargando 3 scripts consecutivos, currentScript.parentElement debería seguir funcionando en todos los navegadores.
      SvelteKit también tuvo esta discusión y al final implementó IDs aleatorios para especificar el elemento de destino: https://github.com/sveltejs/kit/issues/2221
  • Revisando la guía de migración, aprendí algunas cosas que jQuery puede hacer y Cash no, que no conocía y que quizá algún día valga la pena probar.
    https://github.com/fabiospampinato/cash/blob/master/docs/mig...

  • Como objetivo extendido, estaría bueno usar magia de template strings de TypeScript para inferir con precisión el tipo de elemento.
    Por ejemplo, se podría inferir estáticamente que $('div#name') es un HTMLDivElement.

    • Ese paquete se llama typed-query-selector.
      Hay un ejemplo de uso real aquí: https://github.com/GoogleChrome/lighthouse/blob/main/types/i...
    • Elixir y algunos lenguajes pueden lograr cosas así con pattern matching y el sistema de tipos, pero muchos lenguajes no.
      No sé si eso es posible en TypeScript, y no veo bien cómo se podría hacer.
  • Escuché que jQuery 4 es una alternativa a jQuery para navegadores modernos.

  • Al principio usé esto en la extensión de navegador que estoy creando, pero al final me pasé a una biblioteca JSX.
    Cuando pasas del terreno de las “apps simples”, jQuery se convierte rápidamente en código difícil de razonar, y lo digo incluso como alguien que creó una biblioteca inspirada en jQuery.
    Al final hay que usar la herramienta adecuada para el trabajo.
    [1]: https://github.com/aleclarson/dough
    Si puedes manejar bien jQuery en apps medianas o grandes, perfecto, pero no es mi preferencia.

  • Antes, cuando intentaba reducir JS, usaba https://github.com/filamentgroup/shoestring.
    La razón principal era que ofrecía builds personalizados con solo lo que realmente necesitabas.
    Cash parece tener una función similar, aunque está un poco más escondida en la documentación: https://github.com/fabiospampinato/cash/blob/master/docs/par...
    Si lo usara, creo que intentaría primero por ese lado.
    Aun así, sigo pensando que hoy es mejor opción usar directamente lo que ya ofrecen los navegadores.
    En la práctica es bastante bueno, y jQuery ya no es imprescindible.
    Más aún si consideramos que incluso una alternativa pequeña a jQuery pesa 6 kB, mientras que Preact, una biblioteca similar a React, pesa la mitad.

  • No estoy seguro de que ayude mucho más que ponerles alias a las Web APIs que ya existen.