Web Components:原生组件化开发新趋势

程序员的第二曲线
2025-06-16 21:57
阅读 695

开篇背景:为什么我会关注Web Components?

开篇背景:为什么我会关注Web Components?

作为一名有着五年前端开发经验的工程师,我从Vue到React、再到Angular,几乎经历了主流框架的整个迭代过程。但在过去的两年里,我在一个跨平台项目中接触到了Web Components——这个看似“古老”,实则不断进化的技术。

当时我们面临的问题是:多个业务系统之间需要共享一套 UI 组件库,并且这些系统使用的技术栈各不相同(有 React 项目,也有 Vue 项目,甚至还有老的 jQuery 页面)。在这种情况下,维护一套多套适配不同框架的组件变得极其低效。直到团队尝试引入 Web Components,才真正解决了这个问题。

这篇文章,我想结合自己真实的项目经历和踩坑经验,来聊一聊我对 Web Components 的理解和它为何会成为未来组件化开发的新趋势。


遇到的挑战:如何实现跨技术栈的组件复用?

遇到的挑战:如何实现跨技术栈的组件复用?

项目背景

我们公司内部有几个不同的前端团队,各自负责不同的产品模块。虽然大部分都是基于 React 构建的 SPA 系统,但也有一部分遗留的 jQuery 系统在跑。我们需要统一 UI 风格并提高组件可复用性,于是提出构建一个公共组件库。

技术选型前的困境

最开始我们考虑的是基于 React 构建组件库,但很快发现:

  • 在 Vue 或 jQuery 项目中使用 React 组件,必须引入 ReactDOM 和 React,这对性能和体积是一个负担;
  • 使用 Shadow DOM 可以隔离样式,但又担心兼容性和调试成本;
  • 想通过 Iframe 嵌入组件,却发现难以通信和交互;
  • 最终决定采用 Web Components 来作为解决方案的核心。

这听起来有点反主流,但它确实解决了我们的燃眉之急。


解决方案:选择 Web Components 的理由

解决方案:选择 Web Components 的理由

Web Components 并不是一个框架,而是一组浏览器原生支持的标准,包括:

  • Custom Elements:自定义 HTML 元素
  • Shadow DOM:封装组件的 DOM 和 CSS
  • HTML Templates:使用 <template> 标签定义模板
  • ES Modules:加载组件脚本的方式

这些标准让我们能够在不依赖特定框架的情况下,创建真正的跨平台组件。

我们采用了 Lit 这个轻量级框架(由 Google 推出),它对 Web Components 做了非常优雅的封装,同时保留了原生的特性。


实践案例:封装一个通用的 <ui-dropdown> 组件

实践案例:封装一个通用的 <ui-dropdown> 组件

为了让大家更直观地了解我们在做什么,我们先来看一段实际代码示例:

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

export class UiDropdown extends LitElement {
  static properties = {
    options: { type: Array },
    selected: { type: String }
  };

  static styles = css`
    :host {
      display: inline-block;
      position: relative;
    }
    .dropdown-content {
      display: none;
      position: absolute;
      background: white;
      border: 1px solid #ccc;
      padding: 8px;
      z-index: 1000;
    }
    .dropdown-content.show {
      display: block;
    }
  `;

  constructor() {
    super();
    this.options = [];
    this.selected = '';
  }

  render() {
    return html`
      <button @click="${this._toggle}">${this.selected || '请选择'}</button>
      <div class="dropdown-content ${this.open ? 'show' : ''}">
        ${this.options.map(option => html`
          <div @click="${() => this._select(option)}">${option.label}</div>
        `)}
      </div>
    `;
  }

  _toggle() {
    this.open = !this.open;
  }

  _select(option) {
    this.selected = option.label;
    this.open = false;
    this.dispatchEvent(new CustomEvent('change', { detail: option }));
  }
}

customElements.define('ui-dropdown', UiDropdown);

这段代码展示了一个下拉菜单组件的基本结构。我们可以看到几点关键点:

  • 不依赖任何外部状态管理(比如 Redux 或 Vuex);
  • 样式作用域被限定在组件内;
  • 事件通过 CustomEvent 触发,可以在任何页面监听;
  • 可以直接在 HTML 中像这样使用:
<ui-dropdown 
  options='[{"label": "选项一", "value": 1}, {"label": "选项二", "value": 2}]'
  @change="${handleSelect}"
></ui-dropdown>

无论你用的是 jQuery、React 还是 Vue,只要你把这个脚本注册到全局上下文里,就可以在任意地方使用。


踩过的坑:那些令人头疼的细节问题

1. 属性传值类型不对导致数据绑定失败

初期我们以为可以直接给组件传对象或者数组,但实际上如果你写成这样:

<ui-dropdown options="[Object object]" />

因为 HTML 属性本质上是字符串类型,传递 JSON 字符串时需要手动解析:

attributeChangedCallback(name, oldVal, newVal) {
  if (name === 'options') {
    try {
      this.options = JSON.parse(newVal);
    } catch (e) {
      console.error('Invalid options:', newVal);
    }
  }
}

后来我们干脆加了一个工具方法用于自动转换属性值,避免手动处理。

2. Shadow DOM 样式泄漏与调试困难

刚开始用 Shadow DOM 的时候,我们以为样式就真的完全隔离了,结果发现某些全局 CSS 还会影响 Shadow 内部的内容,特别是字体相关设置。

解决方法是:

  • 尽量少依赖外部样式表;
  • 给组件加上 :host-context() 来响应外部样式变化;
  • 使用浏览器开发者工具检查 Shadow DOM,Chrome DevTools 支持得很完善了。

3. IE11 兼容性是个噩梦

是的,Web Components 是现代浏览器的特性,默认并不兼容 IE11。

我们一开始也没有想到会有旧客户还在使用 IE 浏览器访问系统,直到上线后用户反馈下拉组件根本无法渲染。

最后我们采取了几种策略:

  • 引入官方 polyfill:webcomponents-bundle.js
  • 使用 Babel 编译 Lit 生成 ES5 版本;
  • 动态加载 polyfill 的方式判断是否是 IE 浏览器。

不过说实话,IE 上的 Web Components 性能还是不太理想,建议如果是刚需支持 IE11 的项目,还是要谨慎采用。


效果总结:带来了哪些收益?

经过几个月的实际应用,我们逐步将核心组件迁移到 Web Components 方案下,效果还是很明显的。

✅ 跨技术栈复用成为可能

我们终于可以在一个 jQuery 页面里引入一个 <ui-datepicker>,也可以在 React 项目中通过 React DOM 渲染一个 Web Component。再也不需要为每个框架写一套样式和逻辑。

✅ 组件封装更容易做到高内聚低耦合

因为没有外部依赖,所有状态都在组件自身维护,对外只暴露必要的属性和事件接口。这种设计反而让组件更容易测试和维护。

✅ 团队协作效率大幅提升

UI 组件的文档变得更加清晰易懂,只需要说明 props 和 events 即可,不需要解释“你是 Vue?那得用 v-model;你是 React?那你得 useState”。


经验分享:想对你说的心里话

🧠 不要为了“新技术”去用 Web Components

Web Components 确实很强大,但并不是所有项目都适合采用它。如果你的项目只有一个框架(比如纯 React 或者纯 Vue),并且组件规模不大,那么继续用你现有的技术栈完全没有问题。

但如果你面临如下场景:

  • 需要在多个技术栈之间共享组件;
  • 组件需要高度解耦和封装;
  • 希望提升组件可维护性和长期稳定性;

那么 Web Components 是一个非常值得探索的方向。

🔧 开发工具和调试技巧推荐

  • Chrome DevTools 对 Shadow DOM 的支持非常友好,可以右键审查组件查看结构;
  • 使用 VS Code 插件 Lit for VS Code,提供语法高亮和智能提示;
  • 使用 Storybook + Web Components 模板来快速搭建组件文档;
  • 注意打包优化:如果用 Rollup/Webpack,可以配置按需加载、懒加载等方式提升性能。

📦 关于打包和分发

我们最终选择了 Rollup 打包 Lit 组件,因为它更适合 ES Module 的输出格式。如果你的项目希望对外发布 npm 包,也可以参考以下结构:

dist/
├── ui-button.js
└── ui-dropdown.js
src/
└── components/
index.html // 示例页面
README.md
package.json

结语:技术是手段,不是目的

回想最初决定采用 Web Components 的时候,其实团队内部也争议不小。有人认为这太“复古”,有人担心生态不够丰富,但我始终相信一件事:

技术的选择应该围绕解决实际问题展开,而不是追逐热点。

在我们的项目实践中,Web Components 证明了它的价值。它不是万能钥匙,但在合适的场景中,它是目前最为优雅的一种解决方案。

如果你也在寻找一种方式,让你的组件能够“穿越框架”,跨越时间与空间,那么不妨试试 Web Components。

毕竟,它就是写在标准里的东西。


作者寄语:本文是我在实际项目中的真实经验和思考整理,欢迎大家留言交流或指出不足之处。技术路漫漫其修远兮,愿你我也都能在这条路上越走越稳。

评论 0

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