Web Components:原生组件化开发新趋势
——给完全零基础新手的入门指南
大家好,我是你们的技术培训负责人,带过不少刚毕业的应届生。今天之所以写这篇教程,是因为最近在面试中发现一个有趣的现象:很多同学能把 React 用得很溜,却对浏览器原生支持的 Web Components 一无所知。而越来越多大厂(比如 Salesforce、GitHub)正在用它构建高性能、框架无关的 UI 组件库。
我当初学前端时,也是从 jQuery 一路摸爬滚打到 React/Vue,后来才意识到:其实浏览器早就给了我们“造轮子”的能力,根本不需要框架也能做组件化! 今天,我就用最通俗的语言,带你从零开始掌握 Web Components —— 无需任何框架,只需一个浏览器。
一、Web Components 是什么?能用来做什么?
简单说:Web Components 是一套浏览器原生支持的组件化技术。你可以把它理解为“不用 React/Vue 也能写组件”。
- 不用依赖任何框架(比如 React、Vue)
- 跨框架复用:你写的 Web Component,既能在 React 项目里用,也能在 Vue、Angular 甚至纯 HTML 页面里直接使用
- 封装性强:内部逻辑和样式完全隔离,外部无法干扰
📌 举个生活化的例子:
如果把网页比作搭积木,React 是一套“乐高系统”(需要特定底板和连接件),而 Web Components 就像是“通用积木”——无论你用什么底板,都能插上去。
二、为什么现在要学 Web Components?
- 面试加分项:我最近面了几个候选人,问“除了 React,你还知道哪些组件化方案?” 很多人答不上来。如果你能说出 Web Components 并演示,立刻脱颖而出。
- 微前端/跨团队协作利器:不同团队用不同技术栈?用 Web Components 写公共组件,谁都能用。
- 轻量高效:没有虚拟 DOM,性能更接近原生。
💡 小提醒:学 Web Components 不是要取代 React,而是多一种武器。就像你会 Java 也会 JavaScript,工具越多,解决问题越灵活。
三、环境准备:只需要一个浏览器!
Web Components 是浏览器原生支持的,所以不需要安装 Node.js、Webpack、Babel 等复杂工具链。你只需要:
- 一个现代浏览器(Chrome、Edge、Firefox、Safari 均可)
- 一个文本编辑器(VS Code、记事本都行)
第一步:创建你的第一个 HTML 文件
新建一个 index.html,内容如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Web Components 入门</title>
</head>
<body>
<h1>我的第一个 Web Component</h1>
<!-- 我们将在这里插入自定义组件 -->
<my-greeting></my-greeting>
<script>
// 组件定义代码将写在这里
</script>
</body>
</html>
保存后,直接双击用浏览器打开即可!是不是比配 React 环境简单多了?
四、核心概念:三大支柱
Web Components 由三个核心技术组成,缺一不可:
| 技术 | 作用 | 类比 |
|---|---|---|
| Custom Elements(自定义元素) | 定义新的 HTML 标签 | 给 HTML 新增 <button> 这样的标签 |
| Shadow DOM(影子 DOM) | 实现样式和结构的隔离 | 给组件加一个“透明盒子”,外面看不到里面 |
| HTML Templates(模板) | 预定义可复用的 HTML 结构 | 像 Word 模板一样,填数据就能用 |
下面我逐一解释,并配上代码。
4.1 Custom Elements:定义你的专属标签
我们要创建一个叫 <my-greeting> 的组件,显示“Hello, Web Components!”。
// 定义组件类
class MyGreeting extends HTMLElement {
constructor() {
super(); // 必须调用父类构造函数
this.innerHTML = '<p>Hello, Web Components!</p>';
}
}
// 注册自定义元素
customElements.define('my-greeting', MyGreeting);
把这段代码粘贴到 <script> 标签里,刷新页面,你会发现页面上出现了这句话!
✅ 关键点:
- 自定义标签名必须包含连字符
-(如my-button),这是浏览器区分原生标签和自定义标签的方式customElements.define()是注册组件的唯一方式
4.2 Shadow DOM:让样式不被污染
现在有个问题:如果页面其他地方有 p { color: red; },我们的组件文字也会变红!这违背了“组件隔离”的初衷。
解决办法:使用 Shadow DOM。
修改上面的代码:
class MyGreeting extends HTMLElement {
constructor() {
super();
// 创建 Shadow DOM
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
p {
color: blue;
font-size: 20px;
background: #f0f0f0;
padding: 10px;
}
</style>
<p>Hello, Web Components!</p>
`;
}
}
customElements.define('my-greeting', MyGreeting);
现在,无论页面全局怎么改 p 标签样式,这个组件里的文字始终是蓝色背景!
🔍 mode: 'open' vs 'closed':
open:外部可以通过element.shadowRoot访问内部(调试方便)closed:完全封闭,外部无法访问(安全性更高,但调试困难)初学者建议用
open。
4.3 HTML Template:复用结构
如果组件结构很复杂,每次都写 innerHTML 会很乱。这时可以用 <template> 标签预定义结构。
在 <body> 里加一个模板:
<template id="greeting-template">
<style>
p { color: green; font-weight: bold; }
</style>
<p>Hello from Template!</p>
</template>
然后在 JS 中克隆它:
class MyGreeting extends HTMLElement {
constructor() {
super();
const template = document.getElementById('greeting-template');
const content = template.content.cloneNode(true);
this.attachShadow({ mode: 'open' }).appendChild(content);
}
}
customElements.define('my-greeting', MyGreeting);
这样,结构和逻辑就分离了,代码更清晰!
五、实战项目:做一个可交互的“点赞按钮”
光看理论不够,我们来做一个真实的小组件:点击按钮,数字+1,并改变颜色。
步骤 1:HTML 结构
<like-button></like-button>
步骤 2:定义组件
class LikeButton extends HTMLElement {
constructor() {
super();
this.count = 0;
// 创建 Shadow DOM
const shadow = this.attachShadow({ mode: 'open' });
// 定义模板
shadow.innerHTML = `
<style>
.btn {
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
.liked {
background-color: #ff6b6b;
color: white;
}
.normal {
background-color: #ddd;
color: black;
}
</style>
<button class="btn normal">👍 点赞 (<span id="count">0</span>)</button>
`;
// 获取按钮和计数元素
this.button = shadow.querySelector('button');
this.countSpan = shadow.getElementById('count');
// 绑定点击事件
this.button.addEventListener('click', () => {
this.count++;
this.countSpan.textContent = this.count;
this.button.className = 'btn ' + (this.count > 0 ? 'liked' : 'normal');
});
}
}
customElements.define('like-button', LikeButton);
步骤 3:测试
刷新页面,点击按钮,你会发现:
- 数字递增
- 背景变红
- 多个
<like-button>实例互不影响(每个都有自己的count)
✅ 这就是组件化的核心:状态隔离 + 可复用!
六、常见问题 & 避坑指南
Q1:Web Components 和 React 组件有什么区别?
| 对比项 | Web Components | React 组件 |
|---|---|---|
| 依赖 | 无(浏览器原生) | 需要 React 库 |
| 数据传递 | 通过 HTML 属性(attribute)或属性(property) | 通过 props |
| 事件通信 | 自定义事件(CustomEvent) | 回调函数 |
| 生态 | 较小,但增长快 | 非常成熟 |
| 学习成本 | 低(只需 JS + HTML) | 中(需 JSX、状态管理等) |
💡 建议:小型项目、跨框架组件、性能敏感场景 → 选 Web Components;复杂应用、需要丰富生态 → 选 React。
Q2:如何给组件传参数?
比如想设置初始点赞数:
<like-button initial-count="5"></like-button>
在组件中读取:
constructor() {
super();
// 从 attribute 读取
const initial = this.getAttribute('initial-count');
this.count = parseInt(initial) || 0;
}
⚠️ 注意:HTML attribute 只能传字符串,数字/对象需转换。
Q3:如何在 React 中使用 Web Components?
完全可以!React 项目里直接写:
function App() {
return (
<div>
<like-button initial-count="10" />
</div>
);
}
但要注意:React 不会自动监听 Web Component 的自定义事件,需要用 ref 手动绑定:
useEffect(() => {
const button = ref.current;
button.addEventListener('like-change', (e) => {
console.log('点赞数变了:', e.detail.count);
});
}, []);
七、学习建议 & 下一步
📚 推荐学习路径
- 掌握基础:先熟练使用
CustomElement+Shadow DOM - 深入属性与事件:学习
observedAttributes监听属性变化,使用dispatchEvent触发自定义事件 - 结合现代工具:用 Lit(Google 出品的 Web Components 库)简化开发
- 实战项目:尝试封装一个日期选择器、弹窗等通用组件
🎯 面试题挑战(附答案思路)
面试题: “请对比 Web Components 和 React 的组件模型,各自的适用场景是什么?”
回答要点:
- Web Components 是标准,无依赖,适合跨框架、微前端、轻量级组件
- React 提供状态管理、生命周期、生态系统,适合大型应用
- 两者可共存:用 Web Components 做底层 UI 原子组件,React 做业务逻辑层
结语
Web Components 不是新技术(2014 年就提出了),但随着浏览器全面支持,它正成为原生组件化开发的新趋势。作为前端开发者,掌握它不仅能拓宽技术视野,还能在面试和实际项目中多一个解题思路。
我当初学的时候,也觉得“有 React 为啥还要学这个?” 直到参与一个跨团队项目,才体会到它的价值——真正的“一次编写,到处使用”。
希望这篇教程能帮你迈出第一步。记住:最好的学习方式,就是动手写一个组件!
🌟 课后作业:试着做一个
<user-card name="张三" avatar="xxx.jpg">组件,支持传入姓名和头像 URL。完成后,你就算入门啦!
加油,未来的前端高手!

评论 0