4 points par GN⁺ 2024-11-04 | 1 commentaires | Partager sur WhatsApp
  • Cash est une très petite bibliothèque alternative à jQuery qui fournit une syntaxe de style jQuery pour manipuler le DOM dans les navigateurs modernes IE11+
  • Elle exploite les fonctionnalités des navigateurs modernes pour réduire la base de code et permet d’utiliser des méthodes chaînables familières avec une taille de fichier bien plus faible
  • Elle ne vise pas une parité fonctionnelle à 100 % avec jQuery, mais couvre la plupart des cas d’usage du quotidien, et les API implémentées sont globalement compatibles avec jQuery
  • Sa taille est de 6 Ko en minified & gzipped, soit 76,6 % de moins que les 24,4 Ko de jQuery Slim 3.4.1, et elle peut encore être réduite grâce aux builds partiels
  • Elle prend en charge une base de code TypeScript, des types TypeScript générés depuis le code, les événements à espace de noms, ainsi que les builds partiels permettant d’exclure des méthodes individuelles

Le problème que résout Cash

  • Cash est une alternative à jQuery pour les navigateurs modernes, qui fournit le sélecteur $() de style jQuery et des méthodes de collection chaînables pour manipuler le DOM
  • Les navigateurs ciblés sont IE11+
  • L’objectif n’est pas de réimplémenter toutes les fonctionnalités de jQuery à l’identique, mais les fonctionnalités implémentées par Cash sont conçues pour être majoritairement compatibles avec l’API jQuery
  • Les utilisateurs qui migrent depuis jQuery peuvent consulter le guide de migration

Comparaison de taille et de fonctionnalités

  • Dans la comparaison des tailles de fichier, Cash est plus petit que Zepto 1.2.0 et jQuery Slim 3.4.1
    • Unminified : 36,5 Ko
    • Minified : 16 Ko
    • Minified & Gzipped : 6 Ko
  • jQuery Slim 3.4.1 pèse 24,4 Ko en minified & gzipped, et Cash réduit cette taille de 76,6 %
  • Si vous avez besoin d’un bundle plus petit, vous pouvez utiliser les builds partiels
  • Côté fonctionnalités, Cash propose la prise en charge des navigateurs modernes, une maintenance active, les événements à espace de noms, une base de code TypeScript et des types TypeScript générés depuis le code
  • Les builds partiels permettent, avec Cash, d’exclure des méthodes individuelles, tandis que Zepto et jQuery Slim indiquent une exclusion par modules entiers

Utilisation

  • Cash peut être chargé depuis jsDelivr et utilisé directement dans le navigateur
<script src="https://cdn.jsdelivr.net/npm/cash-dom/…;
<script>
  $(function () {
    $('html').addClass ( 'dom-loaded' );
    $('<footer>Appended with Cash</footer>').appendTo ( document.body );
  });
</script>
  • Le paquet npm est disponible sous le nom cash-dom
npm install --save cash-dom
import $ from "cash-dom";

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

Structure de l’API

  • $() est la méthode de sélection centrale de Cash et renvoie une collection de nœuds manipulable
  • Si une fonction est passée, elle est exécutée lorsque le DOM est prêt
  • $() peut recevoir un selector, un nœud DOM, une nodeList, une chaîne HTML, une collection Cash ou un callback document ready
  • Cash propose globalement trois types d’API
    • Sélecteur de requête
    • Méthodes de collection
    • Méthodes de bibliothèque de l’objet global $

Méthodes de collection

  • Les méthodes de collection s’appellent après avoir créé une collection avec $(), par exemple $(element).addClass(className)
  • Les catégories proposées se répartissent en attributs, collections, CSS, données, dimensions, effets, événements, formulaires, manipulation du DOM, offsets et navigation
  • Les principales méthodes incluent notamment
    • Classes/attributs : addClass, removeClass, toggleClass, attr, prop, removeAttr
    • Traitement des collections : add, each, eq, filter, first, get, map, slice
    • Manipulation du DOM : append, prepend, before, after, html, text, clone, remove, replaceWith, wrap
    • Événements : on, off, one, ready, trigger
    • Navigation : find, children, closest, parent, parents, siblings, next, prev
  • Certaines méthodes supplémentaires sont fournies, mais elles sont désactivées par défaut
  • $.fn est le prototype principal des collections et permet d’ajouter des méthodes personnalisées à toutes les collections, à la manière d’un plugin

Méthodes globales de Cash

  • L’objet global $ inclut des méthodes de vérification de type et des utilitaires
  • Les méthodes de vérification de type proposées sont $.isArray, $.isFunction, $.isNumeric, $.isPlainObject et $.isWindow
  • Les utilitaires incluent $.guid, $.each, $.extend, $.parseHTML et $.unique
  • $.extend étend un objet cible avec les propriétés d’objets source et prend aussi en charge l’extension profonde
  • $.parseHTML renvoie une collection à partir d’une chaîne HTML, et $.unique renvoie un nouveau tableau dont les doublons ont été supprimés

Extension et contribution

  • Cash peut être étendu avec des méthodes personnalisées, et la manière de l’étendre est décrite dans extending Cash
  • Les problèmes ou demandes de fonctionnalités peuvent être ouverts via les issues GitHub
  • Le workflow de pull request consiste à cloner le dépôt, installer les dépendances, lancer la recompilation automatique avec npm run dev, exécuter les tests avec npm run test, puis mettre à jour le README si nécessaire
  • La licence est MIT

1 commentaires

 
GN⁺ 2024-11-04
Avis sur Hacker News
  • Les navigateurs sont devenus si bons aujourd’hui que, pour simplifier la manipulation du DOM, ces deux lignes d’alias suffisent souvent
    dqs = document.querySelector.bind(document);
    dqsA = document.querySelectorAll.bind(document);
    On peut donc écrire dqs('#country') au lieu de document.querySelector('#country'), et dqsA('.city') au lieu de document.querySelectorAll('.city')
    Pour le reste, les fonctions natives du navigateur conviennent très bien, et on les importe généralement depuis un module, par exemple import { dqs, dqsA } from '/lib/js/dqs.js';
    https://github.com/no-gravity/dqs.js

    • Les deux lignes qui bindent querySelector/querySelectorAll semblent utiles et raisonnables, mais les importer avec import, c’est excessif
      Ce ne sont que deux lignes simples : inutile d’en faire une dépendance, autant les copier-coller
    • Toutes les fonctions utilitaires ne tiennent pas en une ligne
      Par exemple, pour gérer plusieurs événements, $.fn.one() ou $.fn.on() sont plus faciles à utiliser avec jQuery/Cash, et quand on regarde l’implémentation interne, il y a pas mal de travail : https://github.com/fabiospampinato/cash/blob/master/src/even...
    • querySelectorAll() n’est pas une collection live, donc je le transforme souvent directement en tableau comme ceci
      dqsA = s => Array.from(document.querySelectorAll(s));
      Ainsi, on peut utiliser directement des méthodes de tableau comme .map() ou .filter() sur le résultat, ce qui garde une sensation proche de jQuery
    • J’ai récemment essayé de supprimer une dépendance à React, mais les différences entre navigateurs restaient assez marquées pour la gestion des événements
      Par exemple, pendant les tests, Safari ne déclenchait tout simplement pas l’événement select sur un certain élément, et dans certains navigateurs l’événement ne se déclenchait pas quand seul le curseur se déplaçait
      Même sans avoir besoin des fonctionnalités de React comme les composants, l’état ou les props, les seules fonctions natives du navigateur ne suffisaient pas, et la valeur de React DOM pour masquer les différences entre navigateurs est apparue clairement
    • Cela ressemble à bling.js : https://gist.github.com/paulirish/12fb951a8b893a454b32
  • Une fois presque tous les polyfills retirés, l’avantage durable de jQuery reste selon moi le traitement automatique des listes
    La capacité à décocher tous les boutons d’un formulaire en un seul appel reste difficile à égaler ailleurs, et c’est pareil pour les requêtes sur les parents
    Cela dit, le plus gros problème de l’implémentation, c’est qu’elle échoue silencieusement quand la liste est vide
    J’ai corrigé beaucoup trop de bugs de ce genre, apparus après des refactorings ultérieurs de l’arbre DOM pour des raisons de mise en page ; si je recréais jQuery aujourd’hui, par défaut un ensemble vide provoquerait une erreur, avec des appels chaînés ou un flag pour échouer silencieusement seulement quand on s’en moque vraiment
    J’avais passé quelques heures autrefois à voir si l’on pouvait retirer Sizzle et modifier les choses dans ce sens, mais je ne suis pas allé plus loin
    Au fond, jQuery est aussi lié au vieux débat bibliothèque contre framework, et après avoir fait pendant longtemps des applications monopages avec d’énormes frameworks, j’ai l’impression qu’on approche à nouveau de la vallée de la désillusion

    • Le fait de ne pas avoir à se demander si un sélecteur correspond réellement à des éléments est justement ce qui fait l’attrait de jQuery pour beaucoup de gens
      Quand on demande de masquer tous les .foo, s’ils existent ils sont masqués, sinon rien ne se passe : c’est une approche fire and forget, proche de CSS
      Si l’on écrit .foo { color: red; } et qu’il n’y a pas de .foo dans le document, il n’y a pas d’effet de bord hormis un faible surcoût
    • Dans les navigateurs modernes, modifier tous les éléments d’un résultat de requête peut aussi se faire assez proprement en une ligne
      document.querySelectorAll('input[type=checkbox]').forEach((i) => i.checked = false);
      C’est une approche qui s’appuie sur des NodeList itérables et des helpers d’itérateur
      Beaucoup de requêtes vers les parents peuvent être traitées avec element.closest()
    • Je suis entièrement d’accord sur le fait que le comportement par défaut devrait être strict
      Avec du code basé sur jQuery, chaque développeur doit connaître tous les sélecteurs du projet et les mettre à jour quand le DOM change, ce qui est évidemment impossible
      Remplacer la fonction d’initialisation jQuery par une implémentation maison qui impose une vérification de longueur semble faisable
  • Dans un contexte où les sites web grand public servent littéralement des mégaoctets de JavaScript, je ne vois pas l’intérêt de réécrire toute une bibliothèque avec moins de fonctionnalités pour économiser 50 Ko

    • Indépendamment de ce paquet précis, je suis toujours surpris de voir ce raisonnement dans les discussions sur jQuery
      Beaucoup d’articles s’opposent à l’usage de jQuery à cause de la taille du paquet et des contraintes de bande passante, tout en défendant des frameworks SPA qui consomment bien plus de bande passante
      C’est un raisonnement de culte du cargo totalement absurde
    • Cet argument ressemble à “les chaînes de fast-food grand public vendent des repas à 1 600 calories, alors pourquoi se préparer une salade pour le déjeuner ?” ou “la dette nationale approche les 35 000 milliards de dollars, alors pourquoi comparer les taux de crédit immobilier ?”
      Dans les trois cas, la réponse est la même : le gros problème n’est pas moi, et moi je fais autre chose, de plus petit
      Une autre réponse, c’est que si le problème est d’être trop gros, devenir plus petit ressemble à une solution
      Au fond, la vraie question semble être pourquoi un développeur passerait du temps à réécrire une bibliothèque, mais ce n’est pas si étonnant
      Une grande partie de la programmation consiste à refaire des choses qui existent déjà, parce que le travail l’exige, parce qu’on a besoin d’un comportement ou de caractéristiques de performance légèrement différents, ou simplement parce qu’on veut comprendre comment ça fonctionne
    • Plutôt que de demander “pourquoi s’embêter ?”, accepter les petites dépendances peut aussi pousser les dépendances elles-mêmes à devenir plus petites
    • Les sites web grand public ressemblent surtout à des tas de déchets destinés à diffuser de la publicité, et ne devraient pas servir de référence pour ce que nous devons faire
    • Il y a encore des gens qui essaient d’envoyer l’ensemble de leur JavaScript en moins de 50 Ko
  • Si vous cherchez une alternative à jQuery, j’ai fini par créer ma propre sorte de jQuery après avoir attendu trop longtemps jQuery 4.0, avec quelques différences importantes
    Les animations, tweens et timelines utilisent du CSS pur au lieu du système personnalisé de jQuery, gèrent de façon transparente les éléments uniques et les listes, et visent une utilisation inline

    • La documentation se contredit
      me() est décrit comme renvoyant un seul élément, ou le premier élément, ou null, et any() comme renvoyant un tableau ou un tableau vide
      Mais les exemples plus bas, comme any('button')?.forEach(...) et any('button')?.map(...), suggèrent que cela peut aussi être null
      Je ne sais pas si any() renvoie toujours un tableau comme indiqué plus haut, ou s’il peut aussi renvoyer null comme dans les exemples plus bas
    • C’est bien
      Je m’intéresse particulièrement à la localité du comportement
      Je me demande quelle a été votre expérience avec currentScript.parentElement
      Quand j’avais rapidement regardé le mois dernier, j’avais eu l’impression que ce n’était probablement pas fiable dans certains cas de niche, mais je ne me souviens plus exactement quand
      Je n’ai pas creusé davantage, et je suis content de voir que vous l’avez fait fonctionner correctement
      En supposant que ce ne soit ni async ni module, il me semble que même en chargeant 3 scripts consécutifs, currentScript.parentElement devrait toujours fonctionner dans tous les navigateurs
      SvelteKit a aussi eu cette discussion et a finalement implémenté des ID aléatoires pour désigner l’élément cible : https://github.com/sveltejs/kit/issues/2221
  • En parcourant le guide de migration, j’ai découvert quelques fonctionnalités que jQuery sait faire mais pas Cash, que je ne connaissais pas et qui pourraient servir un jour
    https://github.com/fabiospampinato/cash/blob/master/docs/mig...

  • Comme objectif d’extension, ce serait bien d’utiliser la magie des chaînes de gabarit TypeScript pour inférer précisément le type des éléments
    Par exemple, on pourrait inférer statiquement que $('div#name') est un HTMLDivElement

    • Le package s’appelle typed-query-selector
      Un exemple d’utilisation réel se trouve ici : https://github.com/GoogleChrome/lighthouse/blob/main/types/i...
    • Elixir et quelques autres langages peuvent faire ce genre de choses avec le pattern matching et leur système de types, mais beaucoup de langages ne le peuvent pas
      Je ne sais pas si c’est possible en TypeScript, et je ne vois pas bien comment on pourrait faire
  • J’ai entendu dire que jQuery 4 était une alternative à jQuery pour les navigateurs modernes

  • Je l’ai d’abord utilisé dans une extension de navigateur que je développe, mais j’ai finalement migré vers une bibliothèque JSX
    Dès qu’on dépasse le domaine des « applications simples », jQuery devient vite du code difficile à raisonner, et je le ressens aussi en tant que personne ayant créé une bibliothèque inspirée de jQuery
    Au final, il faut utiliser l’outil adapté au travail
    [1] : https://github.com/aleclarson/dough
    Si vous arrivez à bien gérer jQuery dans des applications moyennes à grandes, très bien, mais ce n’est pas mon goût

  • À l’époque où j’essayais de réduire la quantité de JS, j’utilisais https://github.com/filamentgroup/shoestring
    La raison principale était qu’il proposait des builds personnalisés avec seulement ce dont on avait vraiment besoin
    Cash semble avoir une fonctionnalité similaire, mais elle est un peu plus cachée dans la documentation : https://github.com/fabiospampinato/cash/blob/master/docs/par...
    Si je devais l’utiliser, c’est probablement ce que j’essaierais en premier
    Cela dit, je pense toujours qu’utiliser directement ce que les navigateurs modernes fournissent est un meilleur choix
    En pratique, c’est assez bon, et jQuery n’est plus vraiment indispensable
    D’autant plus que même une petite alternative à jQuery fait 6 ko, alors que Preact, une bibliothèque de type React, fait la moitié de cette taille

  • Je ne suis pas sûr que cela apporte beaucoup plus que de donner des alias à des API Web déjà existantes