利用 Custom Elements 和 SVG 来创建可定制的 UI 组件

在前端开发中,随着 Web 页面越来越复杂,UI 组件也愈加多样化。如果在现有的组件库中没有找到适合的组件,或许可以考虑利用 Custom Elements 和 SVG 来创建可定制的 UI 组件。

Custom Elements

“Custom Elements” 是 Web Components 的核心技术之一。通过它,我们可以创建自定义的 HTML 标签,实现特定的功能需求。

创建 Custom Element

创建 Custom Element 需要分几步:

  1. 定义元素类
  2. 定义元素样式和模板
  3. 注册 Custom Element

例如,我们要创建一个名为 my-button 的按钮组件,可以按照下面的代码来实现。

class MyButton extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });
    shadowRoot.innerHTML = `
      <style>
        button {
          font-size: 1rem;
          padding: 0.5rem 1rem;
          border-radius: 4px;
          color: #ffffff;
          background-color: #007bff;
          border: none;
          cursor: pointer;
        }
      </style>
      <button>
        <slot></slot>
      </button>
    `;
  }
}

customElements.define('my-button', MyButton);

在这个代码中,首先定义了一个 MyButton 类,继承了 HTMLElement 基类,并实现了 constructor 方法。

constructor 方法中:

  1. 调用 super 方法,继承基类的属性和方法
  2. 创建 Shadow DOM,即创建一个封闭的 DOM 树
  3. 将样式和模板插入到 Shadow DOM 中

注意到模板中包含了一个 <slot> 标签,这个标签是用来插入自定义内容的。

最后一句代码调用 customElements.define 方法,注册 Custom Element。

使用 Custom Element

在 HTML 文件中使用 Custom Element 和普通元素一样,只需要使用自定义标签名即可。

<my-button>Hello World!</my-button>

添加属性和事件

为了增强 Custom Element 的灵活性,我们可以为它添加属性和事件。

例如,为 my-button 组件添加 type 属性和 click 事件,代码如下:

class MyButton extends HTMLElement {
  constructor() {
    // ...
    this._type = 'button';
    this._onClick = this._onClick.bind(this);
  }

  static get observedAttributes() {
    return ['type'];
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'type') {
      this._type = newValue;
    }
  }

  connectedCallback() {
    this.addEventListener('click', this._onClick);
  }

  disconnectedCallback() {
    this.removeEventListener('click', this._onClick);
  }

  _onClick() {
    const event = new CustomEvent('my-button-click', {
      bubbles: true,
      composed: true
    });
    this.dispatchEvent(event);
  }

  // ...
}

在这个代码中:

  1. _type 是私有属性,用来存储 type 属性的值,默认为 'button'
  2. _onClick 方法是私有方法,用来处理 click 事件
  3. static get observedAttributes() 方法用来定义需要监听的属性列表
  4. attributeChangedCallback(name, oldValue, newValue) 方法用来监听属性值的变化
  5. connectedCallback() 方法和 disconnectedCallback() 方法分别用来监听组件的生命周期
  6. _onClick() 方法中创建了一个自定义事件 my-button-click,并把它分发到组件的 DOM 树中

注意到在 _onClick() 方法中,我们使用了 bubblescomposed 选项,这样自定义事件可以冒泡到父组件,并且可以穿过 Shadow DOM 边界。

SVG

在前端开发中,使用 SVG 可以轻松地创建矢量图形。下面是一个简单的 SVG 图形:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor">
  <path d="M22 12h-4l-3 9L9 3l-3 9H2" />
</svg>

上面的代码中,<svg> 标签定义了一个 SVG 元素。viewBox 属性用来定义 SVG 坐标系的大小和位置,stroke 属性用来定义路径的颜色和宽度。

<path> 标签定义了一个路径,d 属性用来描述路径的形状。

利用 Custom Elements 和 SVG 创建可定制的 UI 组件

现在,我们有了创建 Custom Element 和 SVG 的基本知识,可以开始实现一个可定制的 UI 组件了。

例如,我们可以创建一个 my-icon-button 组件,它可以接受一个 SVG 图标作为输入,并支持自定义样式和事件。

class MyIconButton extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });
    this._icon = this.querySelector('svg');

    shadowRoot.innerHTML = `
      <style>
        :host {
          display: inline-block;
          vertical-align: middle;
          cursor: pointer;
        }

        svg {
          width: 1.5rem;
          height: 1.5rem;
        }
      </style>
      <slot name="icon">${this._icon.outerHTML}</slot>
    `;
  }

  static get observedAttributes() {
    return ['fill'];
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'fill') {
      this._icon.style.setProperty('fill', newValue);
    }
  }

  connectedCallback() {
    this.addEventListener('click', this._onClick);
  }

  disconnectedCallback() {
    this.removeEventListener('click', this._onClick);
  }

  _onClick() {
    const event = new CustomEvent('my-icon-button-click', {
      bubbles: true,
      composed: true
    });
    this.dispatchEvent(event);
  }
}

customElements.define('my-icon-button', MyIconButton);

在这个代码中,MyIconButton 类用来定义 my-icon-button 组件。在 constructor 方法中,它获取了传入的 SVG 图标,并使用 shadowRoot.innerHTML 方法渲染了组件的 HTML 和 CSS。

注意到在渲染时,我们使用了 <slot> 标签,用来插入传入的 SVG 图标。同时,我们还规定了传入的 SVG 图标要使用 <slot>name 属性,如下所示:

<my-icon-button fill="#007bff">
  <svg slot="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor">
    <path d="M22 12h-4l-3 9L9 3l-3 9H2" />
  </svg>
</my-icon-button>

static get observedAttributes() 方法中,我们定义了需要监听的属性为 fill,这个属性用来定义 SVG 的颜色。

attributeChangedCallback(name, oldValue, newValue) 方法中,我们通过设置 SVG 的 fill 属性来响应属性值的变化。

最后,我们使用 connectedCallback()disconnectedCallback() 方法来监听组件的生命周期,并在 _onClick() 方法中创建自定义事件 my-icon-button-click,并把它分发到组件的 DOM 树中。

总结

通过本文,我们了解了如何利用 Custom Elements 和 SVG 来创建可定制的 UI 组件。Custom Elements 提供了创建自定义 HTML 标签的能力,而 SVG 提供了创建矢量图形的能力。通过结合这两个技术,我们可以创建更加灵活和可定制的 UI 组件,满足不同的业务需求。

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


纠错反馈