Back to Blog

Web Components 为什么火不起来?

Web Components Custom Elements Shadow DOM React SSR
ErpanOmer
ErpanOmer
· 2026-08-26 · 10 min read
Web Components 为什么火不起来?

一听 Web Components ,看起来是很高大上的技术🤔。

它是 W3C 的亲儿子,是浏览器原生支持的组件化标准,由 Custom ElementsShadow DOMHTML Templates 三大底层 API 组成。从 2013 年 Google 首次提出概念到今天,已经过去了整整 13 年。

按理说,一个有着浏览器原生支持、不依赖任何第三方框架、天然跨框架复用的组件标准,应该早就统治前端世界了🤷‍♂️。

但现实是:绝大多数前端工程师依然在用 React 或者 Vue 写组件,Web Components 在实际的商业项目中的采用率,低得令人尴尬。

为什么?因为技术上的正确,从来不等于工程上的好用。接下来我们聊一聊它👇。


开发体验的断崖式落后

Web Components 最大的敌人不是 React,而是它自己那极其原始的开发体验。

我们直接看同一个需求——一个可点击计数器组件——在两套体系下的代码对比:

React 的写法:

function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(c => c + 1)}>点击了 {count} 次</button>;
}

就只有 3 行代码,逻辑清晰,状态驱动视图,任何初级前端都能秒懂🙌。

Web Components 的原生写法:

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 的代码量是 React5 倍以上。而且这里面充斥着极其原始的手动操作:手动拼接 HTML 字符串、手动绑定事件、手动触发重新渲染、手动管理状态。

这不是在写现代化的组件,这是在用 2026 年的浏览器 API 写 2010 年风格的 jQuery 代码😖。


没有内置的响应式系统

ReactuseStateVuerefreactive,它们的核心卖点都是状态驱动视图:你只管改数据,框架自动帮你更新 DOM

Web Components 的标准里,没有任何内置的响应式机制

ChatGPT Image 2026年8月25日 14_02_30.png

你修改了一个属性,DOM 不会自动更新。你必须自己实现 attributeChangedCallback,自己手动去找到对应的 DOM 节点,自己手动去改它的 textContent。当组件的状态变得复杂(比如一个包含 20 个联动字段的表单),你需要手写的同步逻辑会像野草一样疯狂蔓延,最终变成一团无法维护的意大利面条。

有人会说:你可以用 👉 Lit 啊,它给 Web Components 加上了响应式。

没错,Lit 确实极大地改善了开发体验。但问题是:当你必须依赖一个第三方库才能让原生标准变得好用时,这个原生标准的意义在哪里🤷‍♂️? 你用 LitWeb Components,和你用 React 写组件,本质上都是在依赖一个框架。只不过 React 的生态比 Lit 大了几百倍。


Shadow DOM 的样式隔离

Shadow DOMWeb Components 最引以为傲的特性:它提供了真正的样式隔离,组件内部的 CSS 不会泄漏到外部,外部的 CSS 也无法侵入内部。

这在理论上非常美好。但在真实的业务开发中,这种 绝对隔离 会迅速变成不稳定因素。

ChatGPT Image 2026年8月25日 15_37_12.png

当你的设计师说全站的按钮统一用品牌蓝时,你发现你根本无法用一个全局的 CSS 变量去穿透 Shadow DOM 的边界(虽然 CSS Custom Properties 可以穿透,但这需要组件内部主动配合暴露接口,大量第三方 Web Components 并没有做这个工作)。

当你想用 Tailwind CSS 的工具类来快速调整组件的样式时,你发现这些 classShadow DOM 里完全失效,因为 Tailwind 生成的样式表根本注入不到 Shadow Root 里面去😖。

隔离是好事,但不可控的隔离是灾难。 ReactVue 的组件没有 Shadow DOM,但通过 CSS ModulesScoped CSS 或者 Tailwind 就能实现足够好的样式隔离,同时保留了全局主题覆盖的灵活性。


不支持 SSR

在 2026 年,服务端渲染(SSR)已经从可选方案变成了项目的标配。ReactNext.jsVueNuxt,它们的 SSR 生态极其成熟。

ChatGPT Image 2026年8月25日 15_51_52.png

Web ComponentsSSR 支持,至今依然是一个极其尴尬的半成品。

Custom Elements 本质上依赖浏览器的 JavaScript 引擎来注册和执行。在服务端的 Node.js 环境里,根本没有 customElements.define 这个 API。这意味着你的 Web Components 在服务端只能输出一个空壳标签(如 <my-counter></my-counter>),所有的内容都必须等到客户端 JavaScript 加载并执行后才能渲染。

对于一个重视 SEO 和首屏性能的商业项目来说,这是不可接受的硬伤🤔。


框架复用是一个伪需求

Web Components 最大的宣传卖点是:写一次,在任何框架里复用。

这听起来极其诱人。但你冷静想一想:在真实的项目里,你的团队有多少个项目是同时混用 ReactVue 的?

答案几乎是零,使用场景非常少。

绝大多数公司的前端技术栈是统一的。一旦选定了 React,所有项目都用 React;一旦选定了 Vue,所有项目都用 Vue。在单一技术栈的团队里,跨框架复用是一个根本不存在的需求。你为了满足一个伪需求,去承受 Web Components 那极其糟糕的开发体验,这笔账怎么算都不划算🫵。


那它到底适合在哪里呢?

说了这么多缺陷,Web Components 是不是毫无价值?也并不是🖐️。

在一个极其特定的场景下,它依然是无可替代的最优解:大型企业的跨团队基础设计系统。

ChatGPT Image 2026年8月25日 16_00_16.png

当一家公司有几十个前端团队,分别在用 ReactVue、甚至 Angular 时,底层的基础 UI 组件(按钮、输入框、对话框)如果用某个特定框架来写,就必然会排斥其他框架的团队。这个时候,用 Web Components 来构建一套框架无关的基础设计系统,是唯一能让所有团队都无痛接入的方案。

GitHubPrimerAdobeSpectrumSAPUI5——这些顶级企业的设计系统,底层都选择了 Web Components

但请注意,这是一个极少数大型企业才会遇到的问题。对于 99% 的中小型团队来说,直接用 ReactVue 的组件库,永远是性价比最高的选择😀。


一些思考🤔

Web Components 的结果,给所有技术人说明了一件事。

一个技术方案能不能成功,从来不取决于它在标准层面有多正确。W3C 的背书、浏览器的原生支持、理论上的完美架构——这些东西在面对开发日常时,全部苍白无力。

开发者用脚投票。谁的开发体验好、谁的生态强、谁能让我在 deadline 之前把需求交出去,我就用谁🖐️。

ReactVue 赢了,不是因为它们比 Web Components 更正确,而是因为它们更好用,更灵活!

你们同意吗😀?

---

前端技术官-ErpanOmer

喜欢我的 Blog 吗?你也可以部署一份属于你的第三空间!

详细的 🚀部署文档 以帮你安排好! 你的 Star 是对我持续开发的最大鼓励!点击右侧按钮前往 GitHub 支持我。

欢迎 Star !