Custom Elements 与组件化开发的实践

前言

在网页开发中,常常需要将界面元素拆分为多个独立、可复用的组件,从而提高代码的可维护性和可扩展性。随着 Web 技术的不断发展和完善,实现组件化开发的方式也在不断升级。

Custom Elements 是 Web Components 技术中的重要一环,它为开发者提供了一种自定义 HTML 元素的方式,可以将功能、样式、行为封装在一个独立的组件中,然后在 HTML 中像使用原生元素一样使用这个组件。本文将介绍 Custom Elements 的基本概念和实践,在实际开发中结合示例代码详细讲解如何使用 Custom Elements 实现组件化开发。

Custom Elements 简介

Custom Elements 是 Web Components 体系结构的一个重要组成部分,它使开发者可以创建自定义的 HTML 元素,并在其中封装元素的样式、行为和功能。在使用 Custom Elements 创建的自定义元素时,可以像使用原生 HTML 元素一样使用它们,也可以将它们当做组件使用,实现组件化开发。

Custom Elements API 是由 W3C 标准化的,可以在现代浏览器中直接使用,如 Chrome、Safari、Firefox、Edge 等。

自定义元素的生命周期

当浏览器解析到自定义元素的定义时,会依次触发以下生命周期函数:

  • connectedCallback:元素与文档DOM树连接时,触发该函数。
  • disconnectedCallback:元素从文档DOM树中移除时,触发该函数。
  • adoptedCallback:元素从文档DOM树中的一个文档移动到另一个文档时,触发该函数。
  • attributeChangedCallback:元素的一个属性被增加、删除或修改时,触发该函数。

实践

下面通过一个使用 Custom Elements 实现组件化开发的示例来详细介绍如何利用 Custom Elements 创建自定义元素和使用它们。

创建自定义元素

首先我们要创建自定义元素,通常的做法是继承 HTMLElement 类,同时定义元素的样式和行为。下面是一个简单的例子:

<!-- my-button.html -->
<style>
  button {
    background-color: #0093E9;
    color: #FFFFFF;
    padding: 8px 16px;
    border-radius: 4px;
    border: none;
    cursor: pointer;
    font-size: 14px;
    font-weight: 600;
  }
</style>

<!-- 继承 HTMLElement 类 -->
<script>
  class MyButton extends HTMLElement {
    constructor() {
      super();
      // 创建 shadow DOM
      this.attachShadow({mode: 'open'});
      // 定义 shadow DOM 内容
      this.shadowRoot.innerHTML = `
        <button><slot></slot></button>
      `;
      // 添加事件监听器
      this.shadowRoot.querySelector('button').addEventListener('click', () => {
        alert('Hello, Custom Elements!');
      });
    }
  }
  // 定义自定义元素名称和类名
  customElements.define('my-button', MyButton);
</script>

在上面的代码中,我们创建了一个名为 MyButton 的自定义元素,它继承自 HTMLElement 类,并在 constructor 中定义了元素的样式、行为和 shadow DOM 内容。在定义自定义元素时,还要通过 customElements.define 方法将自定义元素名称(这里为 my-button)和类名(这里为 MyButton)进行绑定。

shadow DOM(影子 DOM)是 Custom Elements 中的重要概念,它是一个与文档 DOM 树隔离的 DOM 树,可以用来封装元素的样式和行为。上面的示例中,我们在 constructor 中使用 this.attachShadow 方法创建了一个 shadow DOM,然后通过 this.shadowRoot.innerHTML 定义了 shadow DOM 内容。

<button><slot></slot></button>

在 shadow DOM 中,我们使用了 <slot> 元素来定义插槽(slot),它可以用来插入在外部使用 MyButton 元素时传入的内容。例如,我们可以这样使用 MyButton 元素:

<my-button>Click me!</my-button>

这时,插槽 <slot></slot> 就会被替换为 Click me!

使用自定义元素

创建了自定义元素后,我们可以像使用其他 HTML 元素一样使用它们。例如,我们可以在 HTML 文件中这样使用:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Custom Elements Demo</title>
    <script src="my-button.html"></script>
  </head>
  <body>
    <my-button>Click me!</my-button>
  </body>
</html>

这会显示一个带有“Click me!”文本的蓝色按钮,点击它时弹出“Hello, Custom Elements!”的提示框。

封装组件

自定义元素和组件化开发的本质就是将页面中的元素和行为封装在一个独立的组件中,提高代码的可维护性和可扩展性。在上述示例中,我们已经封装了一个名为 MyButton 的组件。下面再来看一个更加复杂的组件:一个带有搜索框和搜索结果的自定义元素。

<!-- my-search.html -->
<style>
  input {
    border: 1px solid #CCCCCC;
    border-radius: 4px;
    padding: 8px;
    font-size: 14px;
    width: 200px;
  }

  ul {
    border: 1px solid #CCCCCC;
    border-radius: 4px;
    box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1);
    margin-top: 8px;
    padding: 0;
    list-style-type: none;
    max-height: 200px;
    overflow-y: auto;
  }

  li {
    padding: 8px;
    cursor: pointer;
    transition: background-color 0.2s ease;
  }

  li:hover {
    background-color: #EEEEEE;
  }
</style>

<script>
  class MySearch extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({mode: 'open'});
      this.shadowRoot.innerHTML = `
        <input type="text" placeholder="Search...">
        <ul></ul>
      `;
      this.input = this.shadowRoot.querySelector('input');
      this.list = this.shadowRoot.querySelector('ul');
      this.input.addEventListener('input', this.search.bind(this));
    }

    async search() {
      const keyword = this.input.value;
      const data = await fetch(`https://api.github.com/search/repositories?q=${keyword}`)
        .then(res => res.json())
        .catch(err => console.error(err));
      this.render(data.items || []);
    }

    render(items) {
      if (items.length === 0) {
        this.list.innerHTML = '<li>No results found.</li>';
        return;
      }
      this.list.innerHTML = items.map(item => `<li>${item.full_name}</li>`).join('');
      this.list.querySelectorAll('li').forEach(li => {
        li.addEventListener('click', () => {
          alert(li.innerText);
        })
      });
    }
  }

  customElements.define('my-search', MySearch);
</script>

这个示例中,我们创建了一个名为 MySearch 的自定义元素,它包括一个搜索框和搜索结果展示列表。在 constructor 中,我们绑定了输入框的 input 事件到 search 方法上,每当输入框中的内容改变时就触发 search 方法,该方法会向 GitHub 的 API 发起搜索请求,获取结果后调用 render 方法渲染结果列表。

在 render 方法中,我们首先判断返回结果是否为空,如果是则显示“无结果”提示,否则将搜索结果列表添加到 MySearch 中,并为列表中的每一项添加点击事件,当点击某一项时弹出该项的 full_name。

使用 MySearch 组件时,只需要把它放到页面中即可:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Custom Elements Demo</title>
    <script src="my-search.html"></script>
  </head>
  <body>
    <my-search></my-search>
  </body>
</html>

这个示例只使用了一个简单的 API 进行搜索,实际开发中可以结合其他技术和框架,例如 AJAX、React、Vue 等,来实现更复杂的功能和组件。

总结

Custom Elements 是 Web Components 技术中的重要组成部分,它使开发者可以创建自定义的 HTML 元素,并在其中封装元素的样式、行为和功能。使用 Custom Elements 实现组件化开发有助于提高代码的可维护性和可扩展性,在实际开发中应用广泛。

本文介绍了 Custom Elements 的基本概念、生命周期函数、创建自定义元素的方法以及实际开发中的示例代码,希望能对大家理解和应用 Custom Elements 技术有所帮助。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/658fff7deb4cecbf2d589179


纠错
反馈