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 并不是一个框架,而是一组浏览器原生支持的标准,包括:
- Custom Elements:自定义 HTML 元素
- Shadow DOM:封装组件的 DOM 和 CSS
- HTML Templates:使用
<template>标签定义模板 - ES Modules:加载组件脚本的方式
这些标准让我们能够在不依赖特定框架的情况下,创建真正的跨平台组件。
我们采用了 Lit 这个轻量级框架(由 Google 推出),它对 Web Components 做了非常优雅的封装,同时保留了原生的特性。
实践案例:封装一个通用的 <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