用 Web Components 构建跨项目组件库的实战之路

轻舟开发记
2025-06-29 11:33
阅读 760

引言:为什么要从框架转向原生?

引言:为什么要从框架转向原生?

作为一名前端开发工程师,这些年我一直在使用 React、Vue 这样的主流框架进行项目开发。但去年我们团队遇到了一个很典型的问题:多个项目的 UI 组件需要高度复用,但技术栈不统一,React 和 Vue 的组件难以共享,导致大量重复劳动。

一开始,我们尝试用微前端方案把不同技术栈整合到一起,结果却发现通信成本太高,页面加载速度慢,维护也异常复杂。后来,我们尝试了一些“通用”组件库,比如 Angular Elements 或者 Webpack Module Federation,虽然解决了一部分问题,但也带来了额外的构建和部署负担。

直到有一天在一次技术分享会上,我听到了关于 Web Components 的介绍,它那种“原生、轻量、与框架无关”的特性瞬间打动了我。回来后我们团队决定开始尝试这条路,没想到一试就彻底改变了我们的组件开发方式。

今天就来聊聊我们是如何一步步将 Web Components 应用到生产环境中的,以及过程中遇到的一些坑。


问题描述:跨项目组件复用的痛点

问题描述:跨项目组件复用的痛点

场景背景

公司内部同时运行着三个中大型项目:

  • 项目A:使用 React + TypeScript
  • 项目B:使用 Vue3 + Vite
  • 项目C:是一个老的 jQuery 项目,还在用 IE11

这三个项目之间有相当一部分组件是共通的,比如按钮、表单控件、弹窗、导航栏等。我们之前的做法是每个项目都有一套自己的组件库,不仅样式差异大,而且每次功能更新都要重复修改三遍,效率低且容易出错。

更尴尬的是,当产品希望做一个统一风格的后台系统时,前端团队只能手动复制粘贴组件代码,再根据框架做适配,开发周期成倍增长。

核心挑战

  1. 技术栈混杂:React、Vue、jQuery 无法共享组件
  2. 维护成本高:相同逻辑的组件重复开发
  3. 风格不统一:UI 编码规范各自为政,视觉一致性差
  4. 依赖复杂:使用传统组件库时引入不必要的包体积
  5. 升级困难:老项目难以跟进新框架版本

这种状态持续了几个月之后,我们终于下定决心要寻找一种能真正“一劳永逸”的解决方案。


解决方案:用 Web Components 打造独立的组件库

解决方案:用 Web Components 打造独立的组件库

初步探索阶段

我们最初的目标很简单:用一套代码写组件,所有项目都能用

于是我们在 GitLab 上新建了一个 components-core 的仓库,准备用 Web Components 实现基础组件。当时团队里对这项技术都不算熟悉,只是知道它基于浏览器原生 API,不需要依赖特定框架,可以用 <my-button> 这样的自定义标签。

小插曲:第一次失败的尝试

第一次尝试我们选择了最原始的方式:纯 HTML/CSS/JS 写了一个按钮组件。

class MyButton extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
      <button>我是按钮</button>
      <style>
        :host {
          display: inline-block;
        }
        button {
          padding: 8px 16px;
          background: #007bff;
          color: white;
          border: none;
          cursor: pointer;
        }
      </style>
    `;
  }
}

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

这个例子看起来简单,但实际接入项目时问题来了:

  • 没有样式隔离机制,IE11 不支持 shadow DOM,样式冲突严重。
  • 无法传递动态属性,比如 disabled、type 等。
  • 没有事件绑定机制,按钮点击无法向外传递信息。

这说明我们需要更成熟的实践方式。


转型 Lit:让开发更高效

经过调研,我们最终选用了 Lit —— Google 推出的一个轻量级框架,专门用于构建高性能的 Web Components,支持响应式数据绑定、模板语法、良好的类型提示。

我们重写了之前的按钮组件:

import { LitElement, html, css } from 'lit';
import { property } from 'lit/decorators.js';

export class MyButton extends LitElement {
  @property({ type: String }) variant: string = 'primary';
  @property({ type: Boolean }) disabled = false;

  static styles = css`
    :host {
      display: inline-block;
    }

    button {
      padding: 8px 16px;
      font-size: 14px;
      border: none;
      cursor: pointer;
      background-color: var(--btn-bg, #007bff);
      color: var(--btn-color, white);
    }
  `;

  render() {
    return html`
      <button ?disabled=${this.disabled}>
        ${this.variant === 'primary' ? '主按钮' : '次按钮'}
      </button>
    `;
  }
}

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

这样一来,组件具备了完整的可配置能力,还能通过 CSS 变量控制主题色,接入任何项目只需插入标签即可。


组件库搭建与接入流程

我们逐步完成了以下内容:

  1. 基础组件:按钮、输入框、标签页、对话框等
  2. 主题配置:通过 CSS 变量实现不同项目的视觉定制
  3. 构建打包:使用 Vite + rollup 构建 ESModule 输出
  4. 版本管理:通过 npm 私有包发布组件库
  5. 文档站点:搭建 Storybook 展示组件用法和示例

示例:如何在 Vue 项目中使用

只需要安装并注册:

npm install @company/components-core
<template>
  <div>
    <my-button variant="primary" @click="handleClick">提交</my-button>
  </div>
</template>

<script setup>
import '@company/components-core/dist/my-button';
</script>

因为组件本身就是一个 DOM 元素,它完全兼容各类框架甚至 jQuery,只需确保加载顺序即可。


效果总结:性能与协作双赢

效果总结:性能与协作双赢

技术收益

项目 改进点 结果
代码维护 多个项目使用同一组件库 减少约 30% 的 UI 开发时间
包体积 无需引入整个框架 组件大小平均减少 60KB
性能表现 原生组件无虚拟 DOM 首屏渲染更快,特别是移动设备上
兼容性 Polyfill 支持 IE11 老项目可以平滑接入
升级成本 独立于框架 前端技术演进不影响组件库

团队协作提升

  • 所有前端成员统一使用同一个设计语言库
  • 新人入职培训简化,组件使用方式一致
  • 设计师反馈更好,不再出现视觉偏差

经验分享:Web Components 在实战中的注意事项

如果你也在考虑采用 Web Components,我总结了以下几个关键经验,希望能帮助你少踩坑。

✅ 使用 Lit 提升开发体验

虽然原生 Web Components 并不难,但写起来太繁琐,而且缺少响应式编程机制。推荐直接使用 Lit,它提供了模板语法、响应式属性、CSS 集成等非常实用的功能,学习曲线也很平缓。

✅ 用 Shadow DOM 做样式隔离(注意 IE11)

Shadow DOM 是 Web Components 的核心之一,它可以避免样式污染。但在支持 IE11 的项目中需要注意使用 fallback 方案,或者干脆放弃对 IE 的深度兼容(这也是我们最后做的取舍)。

✅ CSS 变量是主题定制的利器

通过暴露 CSS 变量(如 --btn-bg),可以在不同项目中轻松定制样式,而无需重新编译组件。

✅ 事件处理要标准化

Web Components 的事件本质上是原生 CustomEvent,传参要用 detail 字段:

this.dispatchEvent(new CustomEvent('change', {
  detail: { value },
  bubbles: true,
  composed: true
}));

在外部监听事件时也要注意兼容性写法。

✅ 构建工具的选择很重要

我们早期用 Webpack,后来换成 Vite+rollup,打包速度和输出质量提升了不止一个等级。建议选择现代构建工具,优先输出 ESM。

✅ 调试技巧:别忘了 DevTools 支持

Chrome DevTools 已经完整支持 Web Components 的调试,包括 shadow DOM 查看、样式检查、事件监听等,熟练掌握这些技巧会大大提高效率。


尾声:为什么我看好 Web Components

过去一年,我们团队已经用 Web Components 替代了 70% 的跨项目 UI 组件。它的价值不仅仅在于“跨框架”,更重要的是它让我们回到了 Web 开发的本源——使用浏览器自身的能力,而不是被框架绑架。

现在,我们正在尝试在 Web Components 中集成一些 AI 助手相关的交互组件(比如语音识别按钮、自动翻译模块等),这让我更加确信:Web Components 正在成为未来 Web 应用的基础构件。

当然,它不是银弹。如果你的项目全是 React,那继续使用 React Component 也没问题;但如果你面临多技术栈、长生命周期、跨团队协作的场景,Web Components 绝对值得认真考虑。

也许正如我在一次团队分享会上说的那句话:

“真正的组件化,是脱离框架,回归原生。”


欢迎留言交流你的 Web Components 实践经验,或者你正在面临的组件库问题,我们可以一起探讨解决方案。

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝