La dépréciation de vos API de composants Web est un excellent moyen de communiquer à vos utilisateurs qu'une fonctionnalité sera supprimée dans la prochaine version majeure de votre package. Si vous utilisez un manifeste d'éléments personnalisés pour documenter les API de vos composants, cela peut être plus délicat qu'il n'y paraît. Les composants Web ont généralement plus d'API publiques que les composants de framework :
Dans cet article, je vais vous montrer comment documenter les dépréciations de votre API et ajouter des fonctionnalités de remplacement sans introduire de modifications majeures.
L'une des parties les plus délicates du processus de dépréciation est la documentation. Pour les propriétés, les méthodes et même votre composant, cela peut être aussi simple que d'ajouter une balise @deprecated à votre commentaire JSDoc dans la classe du composant.
/** * @deprecated This component is going away. Use ... instead. */ class MyElement extends HTMLElement { /** @deprecated This property is being removed. Use ... instead. */ myProp; /** @deprecated This method is going away. Use ... instead. */ myMethod() { } }
Les variables CSS, les parties CSS, les emplacements, les événements et les attributs sont généralement documentés dans le JSDoc de la classe.
Voici un exemple de ce que vous pouvez documenter dans un JSDoc d'éléments personnalisés :
/** * @attr {boolean} disabled - disables the element * @attribute {string} foo - description for foo * * @csspart bar - Styles the bar element in the shadow DOM * * @slot - This is a default/unnamed slot * @slot container - You can put some elements here * * @cssprop --text-color - Controls the color of foo * @cssproperty [--background-color=red] - Controls the background color of bar * * @prop {boolean} prop1 - some description * @property {number} prop2 - some description * * @fires custom-event - some description for custom-event * @fires {MyType} typed-event - some description for typed-event * @event {MyType} typed-custom-event - some description for typed-custom-event * * @summary This is MyElement * * @tag my-element * @tagname my-element */ class MyElement extends HTMLElement {}
Le défi est que vous ne pouvez pas doubler les balises JSDoc et utiliser la balise @deprecated pour indiquer que ces éléments sont obsolètes. Sinon, cela sera interprété comme si la classe entière est obsolète.
/** * @cssprop --text-color - @deprecated I dub thee "deprecated" ❌ */ class MyElement extends HTMLElement {}
Pour contourner ce problème, j'ai créé un outil (custom-elements-manifest-deprecator) qui vous permet de baliser les éléments dans votre JSDoc afin qu'ils soient mis à jour de manière appropriée dans le manifeste des éléments personnalisés.
En utilisant cet outil, vous pouvez utiliser des balises alternatives pour identifier les API obsolètes. Par défaut, il utilise une balise @deprecated entre parenthèses comme identifiant (c'est ce que nous utiliserons aujourd'hui), mais il peut être personnalisé comme vous le souhaitez.
/** * @cssprop --text-color - (@deprecated) I dub thee "deprecated" ? */ class MyElement extends HTMLElement {}
Une chose importante pour notre équipe est que lorsque nous supprimons ou modifions une API, nous essayons d'introduire la nouvelle fonctionnalité avant la prochaine version majeure afin que les équipes puissent y migrer plus tôt. De cette façon, s’ils restent à jour, ils peuvent minimiser l’impact de la mise à niveau vers une nouvelle version.
Dans cette section suivante, nous verrons comment introduire de nouvelles fonctionnalités sans casser vos anciennes API et comment elles peuvent coexister sans se faire concurrence. Nous mettrons à jour certaines API pour un simple composant de bouton.
Dans cet exemple, j'utiliserai Lit, mais ces fonctionnalités et principes peuvent être appliqués à n'importe quel environnement.
Pour fournir une meilleure saisie semi-automatique et des descriptions dans nos éditeurs comme VS Code et JetBrains, nous devons fournir des noms de variables CSS spécifiques aux composants.
/** * @deprecated This component is going away. Use ... instead. */ class MyElement extends HTMLElement { /** @deprecated This property is being removed. Use ... instead. */ myProp; /** @deprecated This method is going away. Use ... instead. */ myMethod() { } }
La partie délicate est que les équipes utilisent déjà les anciennes variables, nous avons donc besoin des deux pour fonctionner. Nous pouvons le faire en mappant les variables obsolètes avec les nouvelles et en mettant à jour le code de notre bouton pour utiliser uniquement les variables. De cette façon, si les utilisateurs stylisent avec les variables obsolètes, elles seront appliquées aux nouvelles variables, ou les utilisateurs pourront appliquer directement des valeurs aux nouvelles variables.
/** * @attr {boolean} disabled - disables the element * @attribute {string} foo - description for foo * * @csspart bar - Styles the bar element in the shadow DOM * * @slot - This is a default/unnamed slot * @slot container - You can put some elements here * * @cssprop --text-color - Controls the color of foo * @cssproperty [--background-color=red] - Controls the background color of bar * * @prop {boolean} prop1 - some description * @property {number} prop2 - some description * * @fires custom-event - some description for custom-event * @fires {MyType} typed-event - some description for typed-event * @event {MyType} typed-custom-event - some description for typed-custom-event * * @summary This is MyElement * * @tag my-element * @tagname my-element */ class MyElement extends HTMLElement {}
Nous pouvons maintenant mettre à jour nos informations JSDoc avec les nouvelles variables CSS et ajouter (@deprecated) aux anciennes avec des descriptions mises à jour.
/** * @cssprop --text-color - @deprecated I dub thee "deprecated" ❌ */ class MyElement extends HTMLElement {}
Comme nos variables CSS, nous souhaitons fournir des noms avec espace de noms pour nos pièces pour une meilleure prise en charge des outils, nous remplacerons donc le contrôle par le contrôle des boutons. Les parties CSS sont assez simples puisque nous pouvons appliquer plusieurs parties à un élément comme les classes CSS, appliquons donc le nouveau nom de partie à l'élément à côté de l'autre.
/** * @cssprop --text-color - (@deprecated) I dub thee "deprecated" ? */ class MyElement extends HTMLElement {}
Nous pouvons maintenant mettre à jour notre JSDoc avec la nouvelle pièce, rendre obsolète l'ancienne pièce avec (@deprecated) et mettre à jour la description.
/* old variables */ --bg-color: #ccc; --fg-color: black; /* new variables */ --button-bg-color: #ccc; --button-fg-color: black;
Avec les nouvelles initiatives visant à prendre en charge l'internationalisation de nos composants (i18n), nous mettons à jour certaines de nos API pour qu'elles soient plus significatives dans les langues RTL (de droite à gauche). Une chose que nous voulons faire est de mettre à jour notre emplacement qui est destiné à afficher une icône avant le texte du bouton de gauche pour commencer sans interrompre l'expérience pour les projets qui utilisent déjà l'emplacement de gauche.
Nous pouvons le faire en imbriquant l'emplacement obsolète dans le nouvel emplacement. Si le nouvel emplacement n'est pas utilisé, il "retombera" sur l'ancien emplacement.
--bg-color: #ccc; --fg-color: black; --button-bg-color: var(--bg-color); --button-fg-color: var(--fg-color); button { background-color: var(--button-bg-color); border: solid 1px var(--button-fg-color); color: var(--button-fg-color); }
Nous pouvons maintenant mettre à jour notre JSDoc avec le nouveau slot, rendre obsolète l'ancien slot avec (@deprecated) et mettre à jour la description.
/** * An example button element. * * @tag my-button * * @cssprop [--bg-color=#ccc] - (@deprecated) (use `--button-bg-color` instead) controls the background color of the button * @cssprop [--fg-color=black] - (@deprecated) (use `--button-fg-color` instead) controls the foreground/text color of the button * @cssprop [--button-bg-color=#ccc] - controls the background color of the button * @cssprop [--button-fg-color=black] - controls the foreground/text color of the button * */
Pour notre exemple, nous émettons un événement de focus personnalisé, mais cela devient déroutant pour les équipes, nous souhaitons donc ajouter un événement avec un espace de noms (my-focus). les événements sont assez simples puisque vous pouvez émettre les deux événements et que les développeurs peuvent passer au nouveau lorsqu'ils en ont l'occasion.
<button part="control button-control"> <slot></slot> </button>
Nous pouvons maintenant mettre à jour notre JSDoc avec le nouvel événement, rendre obsolète l'ancien événement avec (@deprecated) et mettre à jour la description.
/** * An example button element. * * @tag my-button * * @csspart control - (@deprecated) (use `button-control` instead) provides a hook to style internal button element * @csspart button-control - provides a hook to style internal button element */
REMARQUE : La plupart des outils acceptent @event et @fires pour documenter les événements. Il n'y a pas vraiment de différence entre eux.
Les méthodes sont assez faciles à ajouter en parallèle les unes aux autres et vous pouvez utiliser la balise standard @deprecated dans la description de la méthode pour indiquer qu'elle est obsolète.
/** * @deprecated This component is going away. Use ... instead. */ class MyElement extends HTMLElement { /** @deprecated This property is being removed. Use ... instead. */ myProp; /** @deprecated This method is going away. Use ... instead. */ myMethod() { } }
Les propriétés et les attributs peuvent être documentés dans le JSDoc de la classe à l'aide des balises @attr/@attribute et @prop/@property. Si vous les utilisez, vous pouvez utiliser la balise (@deprecated) pour les rendre obsolètes dans le manifeste des éléments personnalisés. Cependant, il est généralement préférable de documenter la propriété directement en utilisant son propre commentaire JSDoc. Cela permettra à des éléments tels que les types et d'autres outils d'identifier correctement les API obsolètes.
Ce qui est bien, c'est que la plupart des analyseurs savent très bien associer des attributs aux propriétés qui sont définies dans la classe de composant avec des décorateurs ou d'autres configurations, donc si vous dépréciez la propriété, l'attribut associé sera également obsolète.
Dans notre composant de démonstration, nous avons un attribut de désactivation que nous souhaitons mettre à jour en désactivé pour être plus conforme aux éléments HTML natifs.
La première chose que nous voulons faire est de déprécier l'ancienne propriété et d'ajouter notre nouvelle.
/** * @attr {boolean} disabled - disables the element * @attribute {string} foo - description for foo * * @csspart bar - Styles the bar element in the shadow DOM * * @slot - This is a default/unnamed slot * @slot container - You can put some elements here * * @cssprop --text-color - Controls the color of foo * @cssproperty [--background-color=red] - Controls the background color of bar * * @prop {boolean} prop1 - some description * @property {number} prop2 - some description * * @fires custom-event - some description for custom-event * @fires {MyType} typed-event - some description for typed-event * @event {MyType} typed-custom-event - some description for typed-custom-event * * @summary This is MyElement * * @tag my-element * @tagname my-element */ class MyElement extends HTMLElement {}
Maintenant, nous voulons éviter d'avoir à vérifier les deux propriétés à chaque fois que nous devons déterminer si le composant est désactivé. Pour simplifier cela, nous pouvons convertir la propriété obsolète en getter/setter et utiliser la nouvelle propriété comme source de vérité.
/** * @cssprop --text-color - @deprecated I dub thee "deprecated" ❌ */ class MyElement extends HTMLElement {}
Maintenant, chaque fois que l'ancienne valeur est mise à jour, elle mettra automatiquement à jour la nouvelle valeur, il nous suffit donc de vérifier la nouvelle propriété pour déterminer si le composant est désactivé.
/** * @cssprop --text-color - (@deprecated) I dub thee "deprecated" ? */ class MyElement extends HTMLElement {}
Découvrez l'exemple terminé !
La modification des API peut introduire des complications, mais cela ne signifie pas que vous devez arrêter de produire de nouvelles fonctionnalités, car cela peut impliquer des modifications radicales. L’introduction précoce de nouvelles fonctionnalités et la dépréciation des anciennes peuvent être un moyen d’offrir une bonne expérience de développement (DX). Cela offre une voie d'amélioration progressive plutôt que de forcer les équipes à attendre et à apporter des changements massifs d'un seul coup pour profiter des nouvelles fonctionnalités.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!