弃用 Web 组件 API 是向用户传达某个功能将在软件包的下一个主要版本中删除的好方法。如果您使用自定义元素清单来记录组件 API,这可能比看起来更棘手。 Web 组件通常比框架组件拥有更多的公共 API:
在本文中,我将演示如何记录 API 弃用并添加替换功能,而不引入重大更改。
弃用过程中最棘手的部分之一是文档。对于属性、方法,甚至您的组件,这可以像在组件类中的 JSDoc 注释中添加 @deprecated 标签一样简单。
/** * @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() { } }
CSS 变量、CSS 部分、插槽、事件和属性通常记录在类的 JSDoc 中。
以下是您可以在自定义元素 JSDoc 中记录内容的示例:
/** * @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 {}
挑战在于您不能在 JSDoc 标记上加倍并使用 @deprecated 标记来指示这些项目已弃用。否则,它将被解释为整个类已被弃用。
/** * @cssprop --text-color - @deprecated I dub thee "deprecated" ❌ */ class MyElement extends HTMLElement {}
为了解决这个问题,我创建了一个工具(custom-elements-manifest-deprecator),它允许您在 JSDoc 中标记项目,以便它们在自定义元素清单中进行适当更新。
使用此工具,您可以使用替代标签来识别已弃用的 API。默认情况下,它使用括号包裹的 @deprecated 标签作为标识符(这就是我们今天将使用的),但它可以根据您的需要进行自定义。
/** * @cssprop --text-color - (@deprecated) I dub thee "deprecated" ? */ class MyElement extends HTMLElement {}
对于我们团队来说,一件重要的事情是,当我们要删除或更改 API 时,我们会尝试在下一个主要版本之前引入新功能,以便团队可以尽早迁移到它。这样,如果他们保持最新状态,就可以最大限度地减少升级到新版本的影响。
在下一节中,我们将介绍如何在不破坏旧 API 的情况下引入新功能,以及它们如何共存而不相互竞争。我们将更新一些简单按钮组件的 API。
在本例中我将使用 Lit,但这些功能和原则可以应用于任何环境。
为了在 VS Code 和 JetBrains 等编辑器中提供更好的自动完成和描述,我们需要提供特定于组件的 CSS 变量名称。
/** * @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() { } }
棘手的部分是团队已经在使用旧变量,所以我们需要它们都起作用。我们可以通过将已弃用的变量映射到新变量并更新按钮代码以仅使用这些变量来实现此目的。这样,如果用户使用已弃用的变量进行样式设置,它们将应用于新变量,或者用户可以直接将值应用于新变量。
/** * @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 {}
现在我们可以使用新的 CSS 变量更新 JSDoc 信息,并将 (@deprecated) 添加到具有更新描述的旧变量中。
/** * @cssprop --text-color - @deprecated I dub thee "deprecated" ❌ */ class MyElement extends HTMLElement {}
就像我们的 CSS 变量一样,我们希望为我们的部件提供命名空间名称,以获得更好的工具支持,因此我们将用按钮控件替换控件。 CSS 部件非常简单,因为我们可以像 CSS 类一样将多个部件应用到一个元素,所以让我们将新部件名称与其他部件一起应用到该元素。
/** * @cssprop --text-color - (@deprecated) I dub thee "deprecated" ? */ class MyElement extends HTMLElement {}
现在我们可以使用新部分更新 JSDoc,使用 (@deprecated) 弃用旧部分,并更新描述。
/* old variables */ --bg-color: #ccc; --fg-color: black; /* new variables */ --button-bg-color: #ccc; --button-fg-color: black;
随着我们的组件支持国际化 (i18n) 的新举措,我们正在更新一些 API,以便在 RTL(从右到左)语言中更有意义。我们想要做的一件事是更新我们的插槽,该插槽旨在从左侧开始在按钮文本之前显示一个图标,而不会破坏已经使用左侧插槽的项目的体验。
我们可以通过将已弃用的插槽嵌套在新插槽中来实现此目的。如果新插槽没有被使用,它将“回退”到旧插槽。
--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); }
现在我们可以使用新插槽更新 JSDoc,使用 (@deprecated) 弃用旧插槽,并更新描述。
/** * 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 * */
对于我们的示例,我们正在发出一个自定义焦点事件,但它让团队感到困惑,因此我们想要添加一个命名空间事件 (my-focus)。事件非常简单,因为您可以发出这两个事件,并且开发人员可以在有机会时转移到新事件。
<button part="control button-control"> <slot></slot> </button>
现在我们可以使用新事件更新 JSDoc,使用 (@deprecated) 弃用旧事件,并更新描述。
/** * 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 */
注意:大多数工具接受 @event 和 @fires 来记录事件。它们之间其实没有什么区别。
方法很容易相互并行添加,您可以在方法描述中使用标准的 @deprecated 标签来表明它已被弃用。
/** * @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() { } }
属性和特性可以使用 @attr/@attribute 和 @prop/@property 标签记录在类的 JSDoc 中。如果您正在使用它们,则可以使用 (@deprecated) 标记在自定义元素清单中弃用它们,但是,通常最好直接使用属性自己的 JSDoc 注释来记录属性。这将使类型和其他工具等能够正确识别已弃用的 API。
好的一点是,大多数分析器都非常擅长将属性与组件类中定义的属性与装饰器或其他配置相关联,因此如果您弃用该属性,则关联的属性也将被弃用。
在我们的演示组件中,我们有一个禁用属性,我们希望将其更新为禁用,以便更符合原生 HTML 元素。
我们要做的第一件事是弃用旧属性并添加新属性。
/** * @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 {}
现在,我们希望避免每次需要确定组件是否被禁用时都必须检查这两个属性。为了简化这一点,我们可以将已弃用的属性转换为 getter/setter,并使用新属性作为事实来源。
/** * @cssprop --text-color - @deprecated I dub thee "deprecated" ❌ */ class MyElement extends HTMLElement {}
现在,每当旧值更新时,它都会自动更新新值,因此我们只需要检查新属性即可确定组件是否被禁用。
/** * @cssprop --text-color - (@deprecated) I dub thee "deprecated" ? */ class MyElement extends HTMLElement {}
查看完成的示例!
更改 API 可能会带来复杂性,但这并不意味着您需要停止生成新功能,因为它可能涉及重大更改。尽早引入新功能并弃用旧功能可以是提供良好开发人员体验 (DX) 的一种方法。这提供了一条逐步增强的途径,而不是迫使团队等待并立即进行大量更改才能利用新功能。
以上是弃用您的 Web 组件 API的详细内容。更多信息请关注PHP中文网其他相关文章!