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

如何开始构建 Web 组件? - 基础知识介绍 创建 Web 组件究竟需要哪些条件? 让我们“化繁为简” 下一步是什么?

如何开始构建 Web 组件?——基础知识

介绍

创建 Web 组件实际需要哪些条件?

让我们“化繁为简™”

接下来是什么?

介绍

如果你正在阅读这篇文章,你可能至少听说过一些关于 Web Components 的内容,它是一组 Web 标准,允许我们创建自己的可重用 UI 组件,这些组件可以在任何类型的 Web 应用程序中使用,并且所有现代浏览器都原生支持这些组件。

你可能不知道从哪里开始,因为如果说 Web 开发领域有什么既令人惊叹又令人担忧的地方,那就是只要有 API,就会有无数的抽象层来“简化它™”。

当然,Web 组件也不例外,我上次查看时发现大约有二十多个不同的库提供了某种抽象来帮助你创建 Web 组件。

所以,对于任何想要开始构建 Web 组件的人来说,仅仅是找到从哪里入手就相当困难,这就是我在这里的原因。

在本系列文章中(没错,这是一个系列文章!),我将涵盖以下几点:

  1. Web Components 标准的基础知识:我将在本文中进行介绍 😉
  2. 不同的库采用不同的方法来帮助您更高效地创建 Web 组件:我将在单独的文章中介绍每种方法,并尝试简要介绍我能找到的遵循每种方法的大多数库。

请记住,这并非严格意义上的教程,我不会解释如何使用每个库构建 Web 组件,我认为每个库的文档本来就是用来做这些的。

本文的主要目的是帮助刚开始接触 Web Components 的开发者找到一种让他们感到舒适的 Web Components 构建方式。😊

引用贾斯汀·法尼亚尼的话,他是Polymer 项目的成员之一,该项目为推动 Web Components 标准做出了巨大贡献:

[...] Web 组件的核心理念在于,哪种方法(创建 Web 组件的方法)更受欢迎并不重要,甚至是否更受欢迎也无关紧要。只要组件之间能够互操作,我们就能从中受益。

好了,介绍就到此为止,让我们直接进入正题吧。

创建 Web 组件实际需要哪些条件?

为了避免重复其他文章已经提到过的内容,我不会解释构成 Web Components 的所有标准,但如果您需要回顾一下,我建议您查看这篇MDN 文章

了解标准的内容固然很好,但是一个普通的 Web 组件究竟是什么样子的呢?

这里是一个简单的“Hello World”组件的示例代码,如果你不完全理解其中的一些内容,别担心,我们稍后会详细讲解。😉

const template = document.createElement("template");
template.innerHTML = `<div>Hello <span class="name"></span></div>`;

class MyGreeting extends HTMLElement {
  constructor() {
    super();
    this.name = "World";
  }

  // Start - Standard Lifecycle Callbacks
  // This gets triggered when the component first is appended to the document
  connectedCallback() {
    if (!this.shadowRoot) {
      this.attachShadow({ mode: "open" });
      this.shadowRoot.appendChild(template.content.cloneNode(true));
    }
    this._nameSpan = this.shadowRoot.querySelector(".name");
    this._nameSpan.textContent = this.name;
  }
  // This defines which attributes will trigger a callback when they get set on the component
  static get observedAttributes() {
    return ["name"];
  }
  // This callback will get triggered when one of the observedAttributes gets changed
  attributeChangedCallback(attr, oldVal, newVal) {
    if (oldVal !== newVal) {
      this[attr] = newVal;
    }
  }

  // End - Standard Lifecycle Callbacks

  set name(value) {
    this.safeSetAttribute("name", value);
    if (this._nameSpan) {
      this._nameSpan.textContent = value;
    }
  }

  get name() {
    return this.getAttribute("name");
  }

  // a helper function to prevent an endless loop on attribute assignment
  safeSetAttribute(attr, value) {
    if (this.getAttribute(attr) !== value) {
      this.setAttribute(attr, value);
    }
  }
}

window.customElements.define("my-greeting", MyGreeting);
Enter fullscreen mode Exit fullscreen mode

这段简单的代码展示了所有 Web Components 标准的实际应用:

  1. 我们创建一个<template>将用于我们组件的数组。
  2. 我们创建一个继承自原生类的HTMLElement,并将其注册到窗口级别CustomElementRegistry。这样,所有<my-greeting>渲染的标签都会使用我们的类来确定要渲染的内容。
  3. 我们的类包含一些自定义元素生命周期回调,这些回调主要帮助我们了解何时设置、销毁或更新我们的组件。
  4. 我们使用该attachShadowRoot函数为组件创建 Shadow DOM 树。

你可能会觉得这段代码对于看似太少的功能来说有点过于冗长。

你的想法是对的,Web Components 标准,至少就其目前的形式而言,是低级标准,要求你为几乎所有用例都需要的功能编写代码。

让我们“化繁为简™”

这就是我之前提到的抽象概念发挥作用的地方,它们的基本目标都是通过以下方式解决使用各种标准时遇到的痛点:

  1. 提供一个渲染引擎,无需手动操作 DOM。
  2. 可以扩展、包装或编译成一个可以在其中注册的类CustomElementRegistry
  3. 扩展原生生命周期回调,有时还会添加库特定的回调,以帮助处理更多用例,例如状态管理等等。
  4. 处理 Shadow DOM 树的创建,并提供回退方案,例如使用 polyfill 或完全不使用 Shadow DOM。

所有这些抽象通常都能让整体开发体验比使用原生 Web 组件更加愉快。

更棒的是,由于大部分后期处理工作都由标准程序完成,本系列文章中介绍的大多数库在压缩/gzip 后,最终打包后的大小甚至不会增加 10kB!💪

接下来是什么?

到目前为止,我(希望)已经帮助您了解了创建 Web 组件需要哪些条件,以及为什么您可能需要使用库来帮助您获得良好的体验。

但我们别忘了最初的目标,我应该扮演丘比特的角色,帮你找到最适合你的藏书。💘

虽然我提到了库相对于标准提供的许多抽象,但我认为对你最终如何编写组件代码影响最大的,是定义组件“类”的方式。

正如我上面提到的,大多数库都属于以下三种模式之一:

  1. 他们提供了一个类,该类可以扩展HTMLElement并添加额外的功能,以便您可以在代码中扩展该新类。
  2. 它们提供了一个函数,调用该函数时,将为你的组件创建一个包含额外功能和组件代码的类。
  3. 它们提供工具,可以将你的代码(通常以专有语法编写)编译成一个类,用于你的组件,该类既包含额外的功能,又包含你的组件名称。

在接下来的文章中,我将更详细地介绍每种模式的工作原理,并尽量简要地介绍符合该模式的尽可能多的库。

非常感谢您阅读到最后,希望您喜欢这篇文章,也请继续阅读本系列的其他文章。

如果您对本系列的其余部分有任何疑问或建议,请随时留言,特别是关于您希望从我将要介绍的库中了解到哪些类型的数据。

文章来源:https://dev.to/alangdm/where-to-begin-building-web-components-the-basics-3b78