
一听 Web Components ,看起来是很高大上的技术🤔。
它是 W3C 的亲儿子,是浏览器原生支持的组件化标准,由 Custom Elements、Shadow DOM 和 HTML Templates 三大底层 API 组成。从 2013 年 Google 首次提出概念到今天,已经过去了整整 13 年。
按理说,一个有着浏览器原生支持、不依赖任何第三方框架、天然跨框架复用的组件标准,应该早就统治前端世界了🤷♂️。
但现实是:绝大多数前端工程师依然在用 React 或者 Vue 写组件,Web Components 在实际的商业项目中的采用率,低得令人尴尬。
为什么?因为技术上的正确,从来不等于工程上的好用。接下来我们聊一聊它👇。
开发体验的断崖式落后
Web Components 最大的敌人不是 React,而是它自己那极其原始的开发体验。
我们直接看同一个需求------一个可点击计数器组件------在两套体系下的代码对比:
React 的写法:
tsx
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>点击了 {count} 次</button>;
}
就只有 3 行代码,逻辑清晰,状态驱动视图,任何初级前端都能秒懂🙌。
Web Components 的原生写法:
javascript
class MyCounter extends HTMLElement {
constructor() {
super();
this._count = 0;
this._shadow = this.attachShadow({ mode: 'open' });
this._render();
}
_render() {
this._shadow.innerHTML = `
<style>
button { padding: 8px 16px; cursor: pointer; }
</style>
<button>点击了 ${this._count} 次</button>
`;
// 每次重新渲染后,必须手动重新绑定事件,因为 innerHTML 会销毁旧的 DOM 节点
this._shadow.querySelector('button').addEventListener('click', () => {
this._count++;
this._render(); // 手动触发重新渲染,没有任何自动化的响应式机制
});
}
}
customElements.define('my-counter', MyCounter);
同样的功能,原生 Web Components 的代码量是 React 的 5 倍以上 。而且这里面充斥着极其原始的手动操作:手动拼接 HTML 字符串、手动绑定事件、手动触发重新渲染、手动管理状态。
这不是在写现代化的组件,这是在用 2026 年的浏览器 API 写 2010 年风格的 jQuery 代码😖。
没有内置的响应式系统
React 有 useState,Vue 有 ref 和 reactive,它们的核心卖点都是状态驱动视图 :你只管改数据,框架自动帮你更新 DOM。
但 Web Components 的标准里,没有任何内置的响应式机制。

你修改了一个属性,DOM 不会自动更新。你必须自己实现 attributeChangedCallback,自己手动去找到对应的 DOM 节点,自己手动去改它的 textContent。当组件的状态变得复杂(比如一个包含 20 个联动字段的表单),你需要手写的同步逻辑会像野草一样疯狂蔓延,最终变成一团无法维护的意大利面条。
有人会说:你可以用 👉 Lit 啊,它给 Web Components 加上了响应式。
没错,Lit 确实极大地改善了开发体验。但问题是:当你必须依赖一个第三方库才能让原生标准变得好用时,这个原生标准的意义在哪里🤷♂️? 你用 Lit 写 Web Components,和你用 React 写组件,本质上都是在依赖一个框架。只不过 React 的生态比 Lit 大了几百倍。
Shadow DOM 的样式隔离
Shadow DOM 是 Web Components 最引以为傲的特性:它提供了真正的样式隔离,组件内部的 CSS 不会泄漏到外部,外部的 CSS 也无法侵入内部。
这在理论上非常美好。但在真实的业务开发中,这种 绝对隔离 会迅速变成不稳定因素。

当你的设计师说全站的按钮统一用品牌蓝时,你发现你根本无法用一个全局的 CSS 变量去穿透 Shadow DOM 的边界(虽然 CSS Custom Properties 可以穿透,但这需要组件内部主动配合暴露接口,大量第三方 Web Components 并没有做这个工作)。
当你想用 Tailwind CSS 的工具类来快速调整组件的样式时,你发现这些 class 在 Shadow DOM 里完全失效,因为 Tailwind 生成的样式表根本注入不到 Shadow Root 里面去😖。
隔离是好事,但不可控的隔离是灾难。 React 和 Vue 的组件没有 Shadow DOM,但通过 CSS Modules、Scoped CSS 或者 Tailwind 就能实现足够好的样式隔离,同时保留了全局主题覆盖的灵活性。
不支持 SSR
在 2026 年,服务端渲染(SSR)已经从可选方案变成了项目的标配。React 有 Next.js,Vue 有 Nuxt,它们的 SSR 生态极其成熟。

但 Web Components 的 SSR 支持,至今依然是一个极其尴尬的半成品。
Custom Elements 本质上依赖浏览器的 JavaScript 引擎来注册和执行。在服务端的 Node.js 环境里,根本没有 customElements.define 这个 API。这意味着你的 Web Components 在服务端只能输出一个空壳标签(如 <my-counter></my-counter>),所有的内容都必须等到客户端 JavaScript 加载并执行后才能渲染。
对于一个重视 SEO 和首屏性能的商业项目来说,这是不可接受的硬伤🤔。
框架复用是一个伪需求
Web Components 最大的宣传卖点是:写一次,在任何框架里复用。
这听起来极其诱人。但你冷静想一想:在真实的项目里,你的团队有多少个项目是同时混用 React 和 Vue 的?
答案几乎是零,使用场景非常少。
绝大多数公司的前端技术栈是统一的。一旦选定了 React,所有项目都用 React;一旦选定了 Vue,所有项目都用 Vue。在单一技术栈的团队里,跨框架复用是一个根本不存在的需求。你为了满足一个伪需求,去承受 Web Components 那极其糟糕的开发体验,这笔账怎么算都不划算🫵。
那它到底适合在哪里呢?
说了这么多缺陷,Web Components 是不是毫无价值?也并不是🖐️。
在一个极其特定的场景下,它依然是无可替代的最优解:大型企业的跨团队基础设计系统。

当一家公司有几十个前端团队,分别在用 React、Vue、甚至 Angular 时,底层的基础 UI 组件(按钮、输入框、对话框)如果用某个特定框架来写,就必然会排斥其他框架的团队。这个时候,用 Web Components 来构建一套框架无关的基础设计系统,是唯一能让所有团队都无痛接入的方案。
GitHub 的 Primer、Adobe 的 Spectrum、SAP 的 UI5------这些顶级企业的设计系统,底层都选择了 Web Components。
但请注意,这是一个极少数大型企业才会遇到的问题。对于 99% 的中小型团队来说,直接用 React 或 Vue 的组件库,永远是性价比最高的选择😀。
一些思考🤔
Web Components 的结果,给所有技术人说明了一件事。
一个技术方案能不能成功,从来不取决于它在标准层面有多正确。W3C 的背书、浏览器的原生支持、理论上的完美架构------这些东西在面对开发日常时,全部苍白无力。
开发者用脚投票。谁的开发体验好、谁的生态强、谁能让我在 deadline 之前把需求交出去,我就用谁🖐️。
React 和 Vue 赢了,不是因为它们比 Web Components 更正确,而是因为它们更好用,更灵活!
你们同意吗😀?
喜欢我的文章,也欢迎关注我的微信公众号:【前端技术官】。
主要分享:前端架构 · AI 编程 · 职场认知 · 开发者成长

微信扫码关注 👆
不定期更新,不刷屏,聊点真正有用的干货。