发布于 2026-01-06 0 阅读
0

自定义元素或:我如何学会不再担心并爱上 Web 组件

自定义元素或:我如何学会不再担心并爱上 Web 组件

如果你正在阅读这篇文章,并且你是一名 Web 开发人员,那么你很可能曾经编写过前端代码。你可能也制作过一些自定义页面,以及日期选择器、图片轮播或样式按钮等组件。作为一名前端开发人员,你可能已经反复制作过这类组件。例如,如果你需要创建一个样式按钮,你可以在 NPM 上找到超过 1300 个自定义按钮库!

确切地说是 1344 个按钮组件库。

这些按钮大多针对特定框架,例如 Angular、Vue 或 React,这很正常,因为这些是目前 Web 上最流行的框架。但是,如果您发现某个按钮(或其他组件)与您的框架不兼容,该怎么办呢?

我通常的做法是继续尝试下一个库,直到找到满意的为止。然而,有些库,比如Ionic,实在太优秀了,不容错过。问题在于,Ionic 长期以来只支持 Angular,所以如果你使用其他框架,就必须使用非官方的封装库。

应该有一个与框架无关的解决方案!

我们可以用三种与框架无关的方式来处理这个问题。


CSS 方法

可以使用CSS库。Bootstrap就是一个很好的例子

<html>
<head>
 <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
</head>
<body>
 <button type="button" class="btn btn-primary">Primary</button>
 <button type="button" class="btn btn-secondary">Secondary</button>
 <button type="button" class="btn btn-success">Success</button>
 <button type="button" class="btn btn-danger">Danger</button>
 <button type="button" class="btn btn-warning">Warning</button>
 <button type="button" class="btn btn-info">Info</button>
 <button type="button" class="btn btn-light">Light</button>
 <button type="button" class="btn btn-dark">Dark</button>
 <button type="button" class="btn btn-link">Link</button>
 <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js"></script>
 <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js"></script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

如上所示,您可以通过 CDN 在 <head> 中导入 Bootstrap <head>,在 <head> 中添加一些不同的按钮<body>,最后在 <head> 的底部导入一些必要的 JavaScript 库<body>

Bootstrap按钮

最终效果很棒,但需要准备一些东西:

  1. 要让 Bootstrap 正常运行,你不仅需要引入用于组件样式化的 CSS 和用于某些组件自定义行为的 JavaScript 文件。自定义 JavaScript 逻辑本身并没有错,但最终你会需要 Bootstrap 之外的其他 JavaScript 库,例如 jQuery 和 Popper。这会增加应用程序运行时必须加载的额外代码量。
  2. 你最终可能会做出一些非常漂亮的按钮,但你还记得 Bootstrap 使用的所有类吗?我唯一熟悉的类就是网格相关的类。其他的类我都去W3Schools上查(虽然我不太愿意承认)。😅

好的,这算是一个解决方案,但可能不是最佳方案。


JavaScript 方法

另一种解决方案是使用纯 JavaScript,例如 Google Maps 等库就采用了这种技术。

<html>
<head>
 <script src="https://maps.googleapis.com/maps/api/js?callback=initMap" async defer></script>
</head>
<body>
 <div id="map" style="height: 100vh; width: 100vw;"></div>
 <script>
   var map;
   function initMap() {
     map = new google.maps.Map(document.getElementById('map'), {
        center: { lat: -34.397, lng: 150.644 },
        zoom: 8
   });
 }
 </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

使用此方案,您需要在页面的 `<head>` 标签内引入 JavaScript 库<head>。然后,您可以使用 DOM 元素来显示该组件。

Google Maps JS 魔法

这个方案最终看起来更简洁,作为一个技术爱好者,感觉很棒。即便如此,仍然存在一些问题:

  1. 如果你需要在 Angular 和 React 等框架中使用像 Google Maps 这样基于 JavaScript 的库,你可能需要一个封装库才能使用它。为什么呢?现代框架会尽量限制对 DOM 的访问,以便其渲染引擎能够直接操作 DOM,因此不建议这样做。
  2. 更糟糕的是,像这样的基于 JavaScript 的库与服务器端渲染的兼容性很差。

这两个解决方案都……🤮

那么,有没有更好的解决方案呢?


Web组件方法

来自https://www.webcomponents.org :

Web 组件是一组 Web 平台 API,允许您创建新的自定义、可重用、封装的 HTML 标签,以便在网页和 Web 应用程序中使用。自定义组件和小部件基于 Web 组件标准构建,可在现代浏览器中运行,并且可以与任何支持 HTML 的 JavaScript 库或框架一起使用。

这些(神奇的)规范是什么?共有 4 项:自定义元素、HTML 模板、Shadow DOM 和 HTML导入(已弃用)。虽然所有这些规范都很重要,但就我们的目的而言,我们只对自定义元素感兴趣(这也是最容易让人对 Web 组件的概念产生困惑的规范)。

定义元素规范详细说明了如何创建新的 HTML 标签以及如何扩展现有的 HTML 标签。通过扩展内置的 HTMLElement 类,您只需使用 JavaScript、HTML 和 CSS 即可构建可重用的 DOM 元素。最终,您将获得易于在应用程序中重用的模块化代码,并且所需的代码量更少。再也不用记住 500 个不同的类名了!

如果您无法想象为什么要创建自定义元素,请允许我问一下……

  • 难道你们要用 Vue 重做三周前在公司使用 React 时做的那个按钮吗?下个月你们又要换框架吗?
  • 如果你想创建一个像 Ionic 那样的组件库,可以与任何框架一起使用,或者完全不使用框架,那该怎么办呢?
  • 当你在一家大型公司工作时,每个部门都使用不同的产品框架,而公司决定更新品牌风格指南,会发生什么?每个团队都必须制作相同的按钮、导航栏和输入框吗?
  • 如果你喜欢90年代,想让这个<blink>标签重新流行起来呢?

答案:创建自定义元素!

// ES6 Class That Extends HTMLElement
class HelloWorld extends HTMLElement {
 // We Can Have Attributes And Listen To Changes
 static observedAttributes = [name];
 attributeChangesCallback(key, oldVal, newVal) {}

// We Can Get And Set Properties
 set name(val) {}
 get name() {}

// We Have Lifecycle Hooks
 connectedCallBack(){}
 disconnectedCallBack(){}

// We Can Also Dispatch Events!!!!
 onClick() {
 this.dispatchEvent(new CustomEvent(nameChange, {}));
 }
}

// Register to the Browser from `customElements` API
customElements.define(hello-world, HelloWorld);
Enter fullscreen mode Exit fullscreen mode

通过扩展 HTML 元素,您可以定义自定义元素,并执行您期望从现代框架中看到的绝大多数操作:

  • 为元素定义属性,这些属性值可以通过 HTML 标签传递给元素,例如 id 或 class。您还可以根据属性的变化触发回调函数。请注意,您只能传递字符串类型的值。
  • 您的元素具有属性的设置器和获取器,您可以将复杂数据类型(非字符串)传递给您的元素。
  • 使用生命周期钩子来实现元素的创建和销毁。
  • 根据元素中的交互和其他触发器分发事件。

当一切就绪,你构建好了漂亮的元素后,可以通过将要使用的选择器和创建的类传递给该define方法来注册它。


自定义元素实战

以下是一个自定义元素的使用示例:早已弃用的 `<div>`<blink>标签。该元素的逻辑以及将其注册到 DOM 的代码被打包到一个 JavaScript 文件中,该文件通过 CDN 加载<head>。然后,在我们的 ` <div>` 标签中<body>,它<blink>像其他 HTML 元素一样使用。如果您不相信这是一个真正的自定义元素,我建议您查看 TS文件。` <div> <blink>` 标签是一个已注册的元素,可以通过简单的 DOM 操作创建。


如果您对自定义元素感兴趣,想了解更多信息,我推荐以下资源:

如果您对<blink>标签感兴趣,可以在GitHub上找到我的代码,或者在NPM上找到该库的打包版本


想了解我的最新动态,请在Twitter上关注我。如果你想看代码,可以在GitHub上找到我。

文章来源:https://dev.to/michaelsolati/custom-elements-or-how-i-learned-to-stop-worrying-and-love-web-components-2j7c