Web Components:原生组件化开发真的能取代React吗?
大家好,我是小林,一名211高校的计算机专业研二学生,平时喜欢在技术博客上分享前端学习心得。最近很多学弟学妹问我:“现在前端框架这么多,React、Vue满天飞,有没有一种不依赖框架、浏览器原生支持的组件化方式?”——这让我想起自己刚入门时也曾被这个问题困扰。
今天我就带大家深入浅出地了解 Web Components ——这个被很多人称为“前端未来”的原生组件化标准。它不需要 React、Vue,也不用打包工具,却能写出高度复用、封装良好的 UI 组件。更重要的是,它正逐渐成为大厂面试中的新热点,甚至出现在“前端面试题挑战”中!
无论你是零基础小白,还是正在准备面试的求职者,这篇教程都能帮你快速掌握 Web Components 的核心思想与实践方法。
一、什么是 Web Components?它能做什么?
简单来说,Web Components 是一套由 W3C 制定的浏览器原生 API 标准,允许开发者创建可复用、自包含的 HTML 元素(也就是“自定义元素”),就像使用 <div>、<button> 一样自然。
你可能会问:“React 不也能做组件吗?为什么要用原生的?”
关键区别在于:
- React 组件依赖 React 运行时,必须通过 JSX 编译、打包才能运行;
- Web Components 是浏览器内置能力,无需任何框架或构建工具,直接写 HTML + JS 就能用。
我当初学的时候,以为只有用框架才能做组件化,后来才知道浏览器早就给了我们“官方答案”。
能用来做什么?
- 构建跨框架通用的 UI 组件(比如一个弹窗,在 React、Vue、Angular 中都能用)
- 开发微前端架构中的独立模块
- 甚至用于爬虫对抗场景(某些网站用 Web Components 动态渲染内容,增加爬取难度)
二、开发环境准备:零配置启动
Web Components 最大的优势之一就是无需构建工具!你只需要:
- 一个现代浏览器(Chrome、Edge、Firefox、Safari 均已支持)
- 任意文本编辑器(VS Code、Notepad++ 都行)
- 一个
.html文件
✅ 注意:不需要安装 Node.js、Webpack、Vite 等工具(除非你想结合框架使用)
快速验证环境是否支持
新建 test.html,输入以下代码:
<!DOCTYPE html>
<html>
<head>
<title>Web Components 测试</title>
</head>
<body>
<my-greeting name="小林"></my-greeting>
<script>
class MyGreeting extends HTMLElement {
constructor() {
super();
const name = this.getAttribute('name') || '陌生人';
this.innerHTML = `<p>你好,${name}!</p>`;
}
}
customElements.define('my-greeting', MyGreeting);
</script>
</body>
</html>
双击打开该文件,如果看到 “你好,小林!” 字样,说明你的浏览器完全支持 Web Components!
三、三大核心技术:拆解 Web Components
Web Components 由三个核心 API 组成,缺一不可:
| 技术 | 作用 | 类比理解 |
|---|---|---|
| Custom Elements | 定义自定义 HTML 标签 | 就像创造一个新的 <button> |
| Shadow DOM | 实现样式和 DOM 的封装隔离 | 类似 Vue 的 scoped CSS |
| HTML Templates | 声明可复用的 HTML 片段 | 类似 React 的 JSX 模板 |
下面我们逐个讲解。
1. Custom Elements:定义你的第一个组件
使用 customElements.define() 注册一个自定义元素。
class MyButton extends HTMLElement {
constructor() {
super(); // 必须调用父类构造函数
this.innerHTML = '<button>点击我</button>';
this.querySelector('button').addEventListener('click', () => {
alert('按钮被点击了!');
});
}
}
// 注册组件,标签名必须包含连字符(-)
customElements.define('my-button', MyButton);
在 HTML 中使用:
<my-button></my-button>
⚠️ 注意:自定义元素名称必须包含至少一个连字符(如
x-card、ui-modal),这是为了避免与未来 HTML 标准冲突。
2. Shadow DOM:真正的封装隔离
上面的例子有个问题:如果页面全局 CSS 修改了 button 样式,会影响我们的组件。这时候就需要 Shadow DOM。
class MyCard extends HTMLElement {
constructor() {
super();
// 创建 Shadow Root
const shadow = this.attachShadow({ mode: 'open' });
// 内部 HTML
shadow.innerHTML = `
<style>
.card { border: 1px solid #ccc; padding: 16px; }
/* 这些样式不会影响外部 */
</style>
<div class="card">
<slot name="title">默认标题</slot>
<slot>默认内容</slot>
</div>
`;
}
}
customElements.define('my-card', MyCard);
使用方式:
<my-card>
<span slot="title">我的卡片</span>
<p>这里是内容</p>
</my-card>
💡
slot是 Web Components 的“插槽”机制,类似 Vue 的<slot>或 React 的children。
3. HTML Template:预定义结构
虽然可以直接用字符串拼接 HTML,但更推荐使用 <template> 标签:
<template id="my-header-template">
<style>
header { background: #333; color: white; padding: 10px; }
</style>
<header>
<h1><slot></slot></h1>
</header>
</template>
<script>
class MyHeader extends HTMLElement {
constructor() {
super();
const template = document.getElementById('my-header-template');
const content = template.content.cloneNode(true);
this.attachShadow({ mode: 'open' }).appendChild(content);
}
}
customElements.define('my-header', MyHeader);
</script>
这种方式让 HTML 结构更清晰,也便于维护。
四、实战:构建一个“待办事项”组件
现在我们来动手做一个完整的 todo-list 组件,涵盖数据绑定、事件处理和动态更新。
步骤 1:定义组件结构
<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<todo-list></todo-list>
<script src="todo-list.js"></script>
</body>
</html>
步骤 2:编写组件逻辑(todo-list.js)
class TodoList extends HTMLElement {
constructor() {
super();
this.tasks = [];
this.render();
}
render() {
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
.container { max-width: 400px; margin: 20px auto; font-family: Arial; }
input { width: 70%; padding: 8px; }
button { padding: 8px 12px; }
ul { list-style: none; padding: 0; }
li { padding: 8px; border-bottom: 1px solid #eee; }
</style>
<div class="container">
<h2>我的待办事项</h2>
<input type="text" id="new-task" placeholder="输入新任务...">
<button id="add-btn">添加</button>
<ul id="task-list"></ul>
</div>
`;
// 绑定事件
shadow.getElementById('add-btn').onclick = () => this.addTask();
shadow.getElementById('new-task').onkeypress = (e) => {
if (e.key === 'Enter') this.addTask();
};
this.updateList();
}
addTask() {
const input = this.shadowRoot.getElementById('new-task');
const text = input.value.trim();
if (text) {
this.tasks.push({ text, done: false });
input.value = '';
this.updateList();
}
}
updateList() {
const list = this.shadowRoot.getElementById('task-list');
list.innerHTML = this.tasks.map((task, index) => `
<li>
<input type="checkbox" ${task.done ? 'checked' : ''}
onchange="this.parentElement.parentElement.parentElement.__component__.toggleTask(${index})">
<span style="${task.done ? 'text-decoration: line-through;' : ''}">${task.text}</span>
<button onclick="this.parentElement.parentElement.parentElement.__component__.removeTask(${index})">删除</button>
</li>
`).join('');
// 临时挂载组件实例(实际项目建议用事件委托)
this.shadowRoot.host.__component__ = this;
}
toggleTask(index) {
this.tasks[index].done = !this.tasks[index].done;
this.updateList();
}
removeTask(index) {
this.tasks.splice(index, 1);
this.updateList();
}
}
customElements.define('todo-list', TodoList);
📌 提示:上面的
__component__是为了简化事件绑定。在真实项目中,建议使用addEventListener和事件委托来避免全局污染。
打开 index.html,你就能看到一个功能完整的待办事项应用!而且它完全独立,不依赖任何框架。
五、新手常见问题解答
Q1:Web Components 和 React/Vue 冲突吗?
不冲突! 事实上,你可以把 Web Components 当作“通用组件”嵌入到 React 或 Vue 项目中。例如:
// React 中使用 Web Component
function App() {
return <my-card title="来自 React">内容</my-card>;
}
但要注意:React 对自定义属性(props)的支持有限,需用 ref 手动设置。
Q2:SEO 友好吗?对爬虫有影响吗?
由于 Web Components 是客户端渲染(除非配合 SSR),纯静态爬虫可能无法获取其内容。这既是缺点(不利于 SEO),也是优点(可用于反爬虫策略)。
一些网站故意用 Web Components 动态生成关键信息,使得简单爬虫无法抓取——这也是为什么“爬虫”会和 Web Components 出现在同一话题中。
Q3:浏览器兼容性如何?
现代浏览器(Chrome 54+、Firefox 63+、Safari 10.1+、Edge 79+)均支持。IE 不支持,但可通过 Polyfill 降级。
六、学习建议与下一步
Web Components 虽然强大,但不是要取代 React,而是提供了一种标准化的组件化方案。建议你:
✅ 先掌握原生 Web Components,理解组件化的本质
✅ 再对比学习 React/Vue 的组件模型,体会差异
✅ 关注微前端、跨框架组件等高级应用场景
如果你正在准备面试,不妨思考这些问题:
- Web Components 如何实现样式隔离?
- Shadow DOM 的 open 和 closed 模式有什么区别?
- 能否用 Web Components 替代部分 React 组件?优缺点是什么?
这些都可能成为“面试题挑战”中的加分项!
结语
我当初学前端时,总以为必须用框架才能写出“高级”代码。直到接触 Web Components,才明白浏览器本身已经给了我们强大的能力。希望这篇教程能帮你打开新思路——有时候,最“原生”的,才是最自由的。
如果你觉得有帮助,欢迎关注我的技术博客,我会持续更新更多零基础友好的前端教程!
作者:小林|211 计算机研究生|专注前端教学与工程实践

评论 0