Web Component入门手册

174 阅读7分钟

介绍

Web Components是一种标准化的技术,不依赖于特定的框架或库。这意味着开发者可以在不同的Web平台上使用Web Components,无论是使用React、Vue、Angular还是其他框架,都可以轻松集成和重用组件。这种跨平台和框架无关性使开发者能够更灵活地选择和组合技术栈

image.png

它通过结合自定义元素(Custom Elements)、Shadow DOM和HTML模板(HTML Templates)三个主要功能,使开发者能够创建独立的、可重用的组件,并将其嵌入到任何Web应用程序中。

自定义元素(Custom Elements)

自定义元素是Web Components的一个核心概念,它允许开发者定义自己的HTML元素,并赋予这些元素自定义的行为和功能。自定义元素为开发者提供了一种扩展和定制HTML元素的方式,使其更适应特定的需求和应用场景。

自定义元素的概念很类似于原生HTML元素,例如<div><button><input>,但开发者可以定义自己的元素名称,并在其中定义所需的属性、方法和事件。

自定义元素的创建

创建和注册自定义元素的过程包括以下步骤:

  1. 创建自定义元素类: 首先,创建一个自定义元素的类,该类继承自HTMLElement。这个类将定义自定义元素的行为和功能。
class CustomElement extends HTMLElement {
  constructor() {
    super();
    // 初始化逻辑
  }

}
  1. 注册自定义元素: 使用customElements.define()方法注册自定义元素。该方法接受两个参数,第一个参数是自定义元素的名称(必须包含短横线),第二个参数是自定义元素的类。
customElements.define('custom-element', CustomElement);
  1. 使用自定义元素: 注册完成后,可以在HTML中使用自定义元素,就像使用任何其他HTML元素一样。
<custom-element></custom-element>

完整的创建和注册自定义元素的示例代码如下:

class CustomElement extends HTMLElement {
  constructor() {
    super();
    // 初始化逻辑
  }

}

customElements.define('custom-element', CustomElement);
<custom-element></custom-element>

通过以上步骤,自定义元素将被创建、注册和添加到Web页面中。

自定义元素的属性和方法

自定义属性

自定义元素可以定义自己的属性,并且这些属性可以通过HTML属性的方式进行设置和获取。在这里我们向custom-element传个custom-attribute属性

<custom-element custom-attribute="123"></custom-element>

我们可以通过this.getAttribute来获取custom-attribute的值

getAttributeHTMLElement类的实例方法。它允许在自定义元素的实例中获取指定属性名的属性值。通过调用getAttribute方法并传入属性名作为参数,可以检索自定义元素的属性值。

class CustomElement extends HTMLElement {
  constructor() {
    super();
  }

 //生命周期方法connectedCallback,当自定义元素被插入到文档中时会被调用
  connectedCallback() {
    const customAttribute = this.getAttribute('custom-attribute');

    console.log(customAttribute);
  }

}

这样我们就可以使用connectedCallback方法,自定义元素被插入到文档中时获取到传入的属性custom-attribute值,但是如果之后属性custom-attribute的值发生变化了,我们就无法感知到了。

这时我们可以使用observedAttributesattributeChangedCallback方法来动态的监听属性custom-attribute的变化

class CustomElement extends HTMLElement {
  constructor() {
    super();
  }

  static get observedAttributes() {
    // 声明需要观察的自定义属性
    return ['custom-attribute'];
  }

  attributeChangedCallback(name, oldValue, newValue) {
    // 监听自定义属性的变化
    if (name === 'custom-attribute') {
      // 处理属性变化的逻辑
    }
  }

}

observedAttributes是自定义元素类中的一个静态getter方法。它允许开发者声明需要观察的自定义属性列表。当这些属性的值发生变化时,浏览器会自动调用attributeChangedCallback方法。

自定义方法

自定义元素可以定义自己的方法,这些方法可以在元素的JavaScript代码中调用,可以用于处理元素的交互、数据操作等等。

class CustomElement extends HTMLElement {
  constructor() {
    super();
  }

  customMethod() {
    // 自定义方法的逻辑
    console.log("自定义方法被调用啦")
  }

}

调用自定义元素的方法:

const element = document.querySelector('custom-element');
element.customMethod();

自定义元素的属性和方法使开发者能够将复杂的功能和行为封装在独立的元素中,从而实现更灵活、可复用的组件化开发。

自定义元素的生命周期钩子函数

以下是自定义元素生命周期钩子函数,它们是在不同阶段被自动调用的方法,用于执行特定的逻辑。

钩子函数描述
constructor()元素的构造函数,在元素实例化时调用。可以用于执行初始化操作。
connectedCallback()元素插入到文档中时调用。可用于执行与元素连接和初始化相关的逻辑。
disconnectedCallback()元素从文档中移除时调用。可用于执行与元素断开连接相关的清理操作。
attributeChangedCallback(name, oldValue, newValue)元素已观察的属性值发生变化时调用。可用于响应属性变化并执行相应操作。
class CustomElement extends HTMLElement {
  constructor() {
    super();
    // 初始化操作
  }

  connectedCallback() {
    // 元素插入到文档时的操作
  }

  disconnectedCallback() {
    // 元素从文档中移除时的操作
  }

  attributeChangedCallback(name, oldValue, newValue) {
    // 监听属性变化的操作
  }


}


影子DOM(Shadow DOM)

影子 DOM(Shadow DOM)是Web Components技术中的一个重要概念,它提供了一种封装和隔离的机制,用于将组件的样式和结构封装在其内部,与外部的文档结构隔离开来。这意味着组件的样式和结构不会受到外部样式的影响,并且组件的内部结构对外部是不可见的。

首先,需要在自定义元素中创建一个ShadowRoot对象,用于承载影子 DOM。可以使用attachShadow()方法来创建ShadowRoot。该方法接受一个参数,指定ShadowRoot的模式(open或closed):

  • 当ShadowRoot的模式设置为"open"时,外部文档可以通过元素的shadowRoot属性访问到ShadowRoot。外部样式可以影响ShadowRoot内部的样式,而且可以通过CSS选择器选择ShadowRoot内的元素。
  • 当ShadowRoot的模式设置为"closed"时,外部文档无法直接访问到ShadowRoot。元素的shadowRoot属性为null。外部样式无法影响ShadowRoot内部的样式,而且无法通过CSS选择器选择ShadowRoot内的元素。
// 创建ShadowRoot
const shadowRoot = this.attachShadow({ mode: 'closed' });

接下来,在ShadowRoot中定义影子 DOM 的结构和内容。可以使用标准的HTML和CSS语法来创建DOM元素和样式规则

// 定义影子 DOM 内容
shadowRoot.innerHTML = `
  <style>
    /* 样式规则 */
  </style>
  <div>
    <!-- DOM 结构 -->
  </div>
`;

完整的示例代码:

<!DOCTYPE html>
<html>
<head>
  <title>Shadow DOM Example</title>
</head>
<body>
  <h1>Shadow DOM Example</h1>

  <my-element></my-element>

  <script>
    // 定义自定义元素类
    class MyElement extends HTMLElement {
      constructor() {
        super();

        // 创建并附加 ShadowRoot
        const shadowRoot = this.attachShadow({ mode: 'closed' });

        // 定义影子 DOM 内容
        shadowRoot.innerHTML = `
          <style>
            .message {
              color: blue;
            }
          </style>
          <div class="message">Hello, Shadow DOM!</div>
        `;
      }
    }

    // 注册自定义元素
    customElements.define('my-element', MyElement);
  </script>
</body>
</html>

HTML模板(HTML Templates)

结合使用自定义元素和HTML <template> 元素,在Web Components中是一种常见方式。

在HTML文件中定义模板:

<template id="myTemplate">
  <style>
    .my-component {
      background-color: lightblue;
      padding: 10px;
    }
  </style>
  <div class="my-component">
    <h1>Hello, Template Component!</h1>
    <p>This is a template-based web component.</p>
  </div>
</template>

创建自定义元素,并将模板内容作为影子 DOM 内容:

class MyComponent extends HTMLElement {
  constructor() {
    super();

    const template = document.getElementById('myTemplate');
    const shadowRoot = this.attachShadow({ mode: 'open' });

    shadowRoot.appendChild(template.content.cloneNode(true));
  }
}

customElements.define('my-component', MyComponent);

slot的使用

使用 <slot> 元素可以在自定义元素内部创建插槽,允许外部环境传递内容到自定义元素中。插槽可以用于在自定义元素的模板中定义占位符,以接受外部提供的内容。

在定义template时预留了name为title和content的solt

<template id="myTemplate"> 
    <style> .my-component { 
        background-color: lightblue; 
        padding: 10px; 
           } 
    </style> 
    <div class="my-component"> 
        <slot name="title"></slot> 
        <slot name="content"></slot> 
     </div> </template>

使用时,只要slot属性中的值与上面的对应就能插入到相应的位置

<my-component> 
    <h2 slot="title">Welcome to the Slot Component</h2> 
    <p slot="content">This is the content passed through the slot.</p> </my-component>

通过这种方式,我们可以在自定义元素的模板中定义占位符,并通过slot插槽接受外部传递的内容。这使得自定义元素更具灵活性,可以接受不同的内容,从而实现更加定制化的组件。