首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Web Components 封装实战:打造可复用的跨框架组件

Web Components 封装实战:打造可复用的跨框架组件

作者头像
fruge365
发布2025-12-17 09:20:18
发布2025-12-17 09:20:18
4130
举报

Web Components 封装实战:打造可复用的跨框架组件

在 React、Vue、Angular 三分天下的今天,组件化开发早已深入人心。但你是否遇到过这样的场景:公司内部既有 Vue2/3 项目,也有 React 项目,还需要维护一些 jQuery 老系统。为了保持 UI 风格统一,难道要为每个框架都写一套组件库吗?

Web Components 提供了一种标准化的解决方案:它允许你创建可重用的自定义元素,这些元素在任何框架中都能像原生 HTML 标签一样工作。本文将带你从零实现一个 Web Component,并探讨其在微前端与跨框架场景下的应用。

TL;DR

  • 核心三剑客:Custom Elements(自定义标签)、Shadow DOM(样式隔离)、HTML Templates(结构复用)。
  • 生命周期connectedCallback(挂载)、disconnectedCallback(卸载)、attributeChangedCallback(属性变化)。
  • 跨框架:在 Vue 中直接用 <my-btn>,在 React 中需处理自定义事件的兼容性。
  • 最佳实践:推荐使用 Lit 等轻量库简化开发,而非裸写原生 API。

1. 原生实现:手写一个 UserCard 组件

我们来实现一个 <user-card> 组件,支持 avatarname 属性,且样式不被外部污染。

代码语言:javascript
复制
// 1. 定义 HTML 模板
const template = document.createElement('template');
template.innerHTML = `
  <style>
    .card {
      display: flex;
      align-items: center;
      padding: 16px;
      border: 1px solid #eee;
      border-radius: 8px;
    }
    img {
      width: 50px;
      height: 50px;
      border-radius: 50%;
      margin-right: 16px;
    }
    h3 { margin: 0; font-size: 18px; }
  </style>
  <div class="card">
    <img />
    <div>
      <h3></h3>
      <slot name="desc"></slot> <!-- 插槽支持 -->
    </div>
  </div>
`;

// 2. 创建自定义类
class UserCard extends HTMLElement {
  constructor() {
    super();
    // 开启 Shadow DOM,实现样式隔离
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.appendChild(template.content.cloneNode(true));
  }

  // 3. 监听属性变化
  static get observedAttributes() {
    return ['avatar', 'name'];
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'avatar') {
      this.shadowRoot.querySelector('img').src = newValue;
    } else if (name === 'name') {
      this.shadowRoot.querySelector('h3').innerText = newValue;
    }
  }
}

// 4. 注册组件
window.customElements.define('user-card', UserCard);
使用方式

无论是在 React、Vue 还是纯 HTML 中,都可以直接这样用:

代码语言:javascript
复制
<user-card avatar="https://api.uomg.com/api/rand.avatar" name="Fruge">
  <p slot="desc">Senior Frontend Developer</p>
</user-card>

2. 核心技术深度解析

Shadow DOM:真正的样式隔离

在 Web Components 之前,为了防止样式冲突,我们不得不使用 BEM 命名规范或 CSS Modules。Shadow DOM 从浏览器层面解决了这个问题:

  • 外部进不去:全局 CSS 无法影响 Shadow Root 内部(除了 CSS 变量)。
  • 内部出不来:组件内的样式也不会污染全局。
Slot 插槽机制

Web Components 的插槽机制与 Vue 非常相似(事实上 Vue 的插槽设计灵感正来源于此):

  • <slot>:默认插槽。
  • <slot name="xxx">:具名插槽。

3. 进阶:使用 Lit 简化开发

裸写原生 API 比较繁琐(如手动 diff 更新 DOM)。Google 推出的 Lit 库(前身是 Polymer)极大地简化了这一过程,它基于 lit-html 渲染引擎,体积极小。

代码语言:javascript
复制
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';

@customElement('simple-counter')
export class SimpleCounter extends LitElement {
  static styles = css`
    button { color: blue; }
  `;

  @property({ type: Number })
  count = 0;

  render() {
    return html`
      <p>Count: ${this.count}</p>
      <button @click="${this._increment}">+</button>
    `;
  }

  private _increment() {
    this.count++;
    // 触发自定义事件
    this.dispatchEvent(new CustomEvent('count-changed', {
      detail: this.count,
      bubbles: true,
      composed: true // 允许穿透 Shadow DOM
    }));
  }
}

4. 框架集成指南

Vue (2.x / 3.x)

Vue 对 Web Components 的支持非常完美。

配置:在 Vite/Webpack 中配置 compilerOptions.isCustomElement,告诉 Vue 编译器哪些标签是自定义元素,不要报错。

绑定

代码语言:javascript
复制
<simple-counter :count="count" @count-changed="handleCount" />
React (16/17/18)

React 在 19 之前对 Web Components 的支持略显尴尬,主要在于事件系统属性传递

  • 属性:React 会将所有 props 当作 HTML Attribute(字符串)传递,无法传递复杂对象(数组/对象)。需使用 ref 手动赋值。
  • 事件:React 的合成事件系统无法监听 Web Component 的自定义事件。需使用 useRef + addEventListener
  • React 19+:已全面改善对 Web Components 的支持,上述问题基本解决。

5. 适用场景与局限性

适用场景
  1. 跨技术栈组件库:公司内部有多种技术栈,需要统一的 UI 基础组件(Button, Icon, Input)。
  2. 微前端:在微前端架构中,子应用可能使用不同框架,Web Components 是天然的隔离边界。
  3. 嵌入式微件:如聊天窗口、反馈按钮,可直接嵌入任意第三方网站。
局限性
  1. SSR 支持:Shadow DOM 的服务端渲染(SSR)目前仍处于实验阶段(Declarative Shadow DOM)。
  2. 样式定制:由于强隔离,外部很难通过 CSS 覆盖内部样式,通常需要组件暴露大量的 CSS Variables 供外部调整。
  3. 无障碍性 (A11y):需要手动处理 Shadow DOM 内部的焦点管理和 ARIA 属性。

6. 总结

Web Components 不是为了取代 React/Vue,而是为了补充组件互操作性的缺失。在构建设计系统(Design System)或跨框架微前端应用时,它是一个极具前瞻性的选择。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-12-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Web Components 封装实战:打造可复用的跨框架组件
    • TL;DR
    • 1. 原生实现:手写一个 UserCard 组件
      • 使用方式
    • 2. 核心技术深度解析
      • Shadow DOM:真正的样式隔离
      • Slot 插槽机制
    • 3. 进阶:使用 Lit 简化开发
    • 4. 框架集成指南
      • Vue (2.x / 3.x)
      • React (16/17/18)
    • 5. 适用场景与局限性
      • 适用场景
      • 局限性
    • 6. 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档