Web Components实战开发:从Custom Elements到Shadow DOM的组件封装全流程

Web Components技术栈核心组成与浏览器支持

Web Components是W3C标准化的浏览器原生组件方案,由Custom Elements、Shadow DOM、HTML Templates和ES Module Imports四个规范组成。与React/Vue等框架不同,Web Components不依赖任何框架运行时,组件可在任何页面直接使用,天然跨框架复用。Chrome、Firefox、Safari、Edge均已完整支持。

Custom Elements允许注册自定义HTML标签,Shadow DOM提供样式隔离与DOM封装,HTML Templates定义可克隆的DOM模板。三者的组合使得组件具有独立作用域、样式不泄漏、DOM结构不暴露的特性。

Custom Elements注册与生命周期回调实战

Custom Elements通过customElements.define()注册,支持autonomous(独立标签)和customized built-in(扩展原生标签)两种模式。生命周期回调包括connectedCallback、disconnectedCallback、adoptedCallback和attributeChangedCallback。

class StarRating extends HTMLElement {
  static get observedAttributes() {
    return ['value', 'max', 'readonly'];
  }

  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this._value = 0;
    this._max = 5;
    this._readonly = false;
  }

  connectedCallback() {
    this._value = Number(this.getAttribute('value')) || 0;
    this._max = Number(this.getAttribute('max')) || 5;
    this._readonly = this.hasAttribute('readonly');
    this.render();
  }

  attributeChangedCallback(name, oldVal, newVal) {
    if (oldVal === newVal) return;
    switch(name) {
      case 'value': this._value = Number(newVal) || 0; break;
      case 'max': this._max = Number(newVal) || 5; break;
      case 'readonly': this._readonly = newVal !== null; break;
    }
    if (this.shadowRoot) this.render();
  }

  render() {
    const stars = Array.from({length: this._max}, (_, i) => {
      const filled = i < this._value;
      return '<span class="star ' + (filled ? 'filled' : '') +
        '" data-index="' + i + '">' + (filled ? '\u2605' : '\u2606') + '</span>';
    }).join('');

    this.shadowRoot.innerHTML = `
      <style>
        :host { display: inline-flex; gap: 2px; cursor: pointer; }
        .star { font-size: 24px; color: #ccc; transition: color 0.2s; }
        .star.filled { color: #f5a623; }
        .star:hover { color: #f5a623; }
      </style>
      ${stars}
    `;

    if (!this._readonly) {
      this.shadowRoot.querySelectorAll('.star').forEach(star => {
        star.addEventListener('click', () => {
          this._value = Number(star.dataset.index) + 1;
          this.setAttribute('value', this._value);
          this.dispatchEvent(new CustomEvent('change', {
            detail: { value: this._value },
            bubbles: true
          }));
        });
      });
    }
  }
}

customElements.define('star-rating', StarRating);

Shadow DOM样式隔离与CSS自定义属性穿透

Shadow DOM的核心价值是样式隔离:组件内部CSS不会影响外部,外部样式也不会穿透进组件。但CSS自定义属性(Custom Properties)天然穿透Shadow DOM边界,这是组件主题化的关键机制。

class ThemeButton extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
  }

  connectedCallback() {
    this.shadowRoot.innerHTML = `
      <style>
        :host {
          --btn-bg: var(--theme-primary, #1a73e8);
          --btn-color: var(--theme-on-primary, #fff);
          --btn-radius: var(--theme-radius, 6px);
          --btn-padding: var(--theme-padding, 8px 20px);
          display: inline-block;
        }
        button {
          background: var(--btn-bg);
          color: var(--btn-color);
          border: none;
          border-radius: var(--btn-radius);
          padding: var(--btn-padding);
          font-size: 14px;
          cursor: pointer;
          transition: opacity 0.2s;
        }
        button:hover { opacity: 0.85; }
        button:active { opacity: 0.7; }
      </style>
      <button><slot></slot></button>
    `;
  }
}
customElements.define('theme-button', ThemeButton);

Web Components与React/Vue框架集成方案

Web Components可直接在React和Vue项目中使用。React 18+对Web Components的支持已大幅改善,Vue 3通过v-is指令或直接注册原生元素即可使用。

// React中使用Web Components
function App() {
  const ratingRef = useRef();

  useEffect(() => {
    const handler = (e) => {
      console.log('评分变更:', e.detail.value);
    };
    ratingRef.current?.addEventListener('change', handler);
    return () => ratingRef.current?.removeEventListener('change', handler);
  }, []);

  return (
    <div>
      <star-rating ref={ratingRef} value={3} max={5}></star-rating>
      <theme-button>确认</theme-button>
    </div>
  );
}

// Vue 3中使用Web Components
// main.js:
// app.config.compilerOptions.isCustomElement = tag => tag.includes('-')

Web Components不替代框架而是互补:用Web Components封装跨项目复用的基础UI组件(按钮、输入框、弹窗),框架继续负责应用状态管理、路由和业务逻辑。这种分层策略在企业微前端架构中尤其有效,各子应用可独立使用不同框架,共享组件层通过Web Components统一。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/webcomponents-shi-zhan-kai-fa-cong-customelements-dao/

(0)
小编小编
上一篇 6小时前
下一篇 6小时前

相关推荐