Web Components:原生组件化开发新趋势
——一个文科生的零基础入门指南
大家好,我是小林,一个从中文系自学转码、如今做前端讲师的“非典型程序员”。当初我学前端时,被各种框架绕得晕头转向,直到后来接触了 Web Components,才发现:原来浏览器早就给我们准备了一套“原生”的组件化方案!今天,我就用最通俗的语言,带完全零基础的朋友一起走进 Web Components 的世界。
为什么我要写这篇教程?
在教学中,我发现很多初学者一上来就学 React、Vue,结果连“什么是组件”都没搞清楚。而 Web Components 是浏览器原生支持的技术,不需要任何构建工具,就能实现真正的组件化。更重要的是——它性能极佳,因为没有额外的运行时开销。
这篇文章将带你:
- 理解 Web Components 的核心价值
- 动手写一个可复用的按钮组件
- 对比它与 React 的异同
- 避开我当初踩过的坑
一、Web Components 是什么?能做什么?
简单说:Web Components 是一套浏览器原生的 API,让你可以像搭积木一样创建自定义 HTML 标签。
比如,你可以写 <my-button>、<user-card>,这些标签自带样式和逻辑,还能在任何项目里复用——无论你用的是 React、Vue,还是纯 HTML!
✅ 优势:
- 原生支持,无需打包
- 真正的封装(样式、逻辑隔离)
- 跨框架兼容(React/Vue/Angular 都能用)
- 性能高(无虚拟 DOM,直接操作真实 DOM)
二、环境准备:5 分钟搞定开发环境
好消息是:Web Components 不需要安装任何依赖!
你只需要:
- 一台电脑
- 一个现代浏览器(Chrome/Firefox/Edge 最新版)
- 任意文本编辑器(推荐 VS Code)
开发流程:
- 新建一个文件夹,比如
web-components-demo - 创建
index.html - 直接在 HTML 中编写或引入 JS 文件
🚫 无需 Node.js、Webpack、Babel!这是它和 React 最大的区别之一。
三、核心概念:三个关键技术点
Web Components 由三部分组成:
| 技术 | 作用 | 类比 |
|---|---|---|
| Custom Elements | 定义自定义 HTML 标签 | 给 HTML “造新词” |
| Shadow DOM | 实现样式和 DOM 的隔离 | 给组件“上锁”,外面看不到里面 |
| HTML Templates | 声明可复用的模板结构 | 写好“模具”,随时复制 |
下面我用最简单的例子逐个解释。
1. Custom Elements:定义你的第一个组件
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<title>My First Web Component</title>
</head>
<body>
<!-- 使用自定义标签 -->
<hello-world></hello-world>
<script>
// 定义组件类
class HelloWorld extends HTMLElement {
constructor() {
super();
this.textContent = "你好,Web Components!";
}
}
// 注册组件
customElements.define('hello-world', HelloWorld);
</script>
</body>
</html>
打开这个 HTML 文件,你会看到页面显示:“你好,Web Components!”。
🔍 注意:
- 自定义标签名必须包含 连字符
-(如my-button),这是规范- 必须继承
HTMLElement- 必须调用
customElements.define()注册
2. Shadow DOM:隔离样式,避免污染
如果不用 Shadow DOM,组件内部的 CSS 可能会影响全局。试试看:
class MyButton extends HTMLElement {
constructor() {
super();
// 创建 Shadow Root(关键!)
const shadow = this.attachShadow({ mode: 'open' });
// 内部 HTML
shadow.innerHTML = `
<button>点我</button>
<style>
button {
background: blue;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
}
</style>
`;
}
}
customElements.define('my-button', MyButton);
现在你在 HTML 中使用 <my-button></my-button>,按钮会变成蓝色,但页面其他按钮不受影响!
💡
mode: 'open'表示外部 JS 可以访问 Shadow DOM(调试方便),closed则完全封闭。
3. HTML Template:预定义结构(可选但推荐)
虽然可以直接用字符串拼 HTML,但更规范的做法是用 <template>:
<template id="my-card-template">
<div class="card">
<h3></h3>
<p></p>
</div>
<style>
.card { border: 1px solid #ccc; padding: 10px; }
</style>
</template>
<script>
class UserCard extends HTMLElement {
constructor() {
super();
const template = document.getElementById('my-card-template');
const content = template.content.cloneNode(true);
this.attachShadow({ mode: 'open' }).appendChild(content);
// 设置内容
this.shadowRoot.querySelector('h3').textContent = this.getAttribute('name') || '匿名';
this.shadowRoot.querySelector('p').textContent = this.getAttribute('bio') || '暂无介绍';
}
}
customElements.define('user-card', UserCard);
</script>
<!-- 使用 -->
<user-card name="小林" bio="前端讲师,文科生转码"></user-card>
这样,结构和逻辑分离更清晰!
四、实战项目:做一个可复用的通知组件
现在我们综合前面知识,做一个 <toast-message> 组件:点击按钮弹出通知。
步骤 1:HTML 结构
<!DOCTYPE html>
<html>
<body>
<button onclick="showToast()">显示通知</button>
<toast-message id="myToast"></toast-message>
<script src="toast.js"></script>
<script>
function showToast() {
document.getElementById('myToast').show('操作成功!');
}
</script>
</body>
</html>
步骤 2:编写 toast.js
class ToastMessage extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<div id="toast" style="
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: #333;
color: white;
padding: 12px 24px;
border-radius: 4px;
opacity: 0;
transition: opacity 0.3s;
pointer-events: none;
">
</div>
`;
}
show(message) {
const toast = this.shadowRoot.getElementById('toast');
toast.textContent = message;
toast.style.opacity = '1';
// 2秒后隐藏
setTimeout(() => {
toast.style.opacity = '0';
}, 2000);
}
}
customElements.define('toast-message', ToastMessage);
✅ 试试点击按钮!你会发现通知从底部弹出,2秒后消失,且样式完全隔离。
五、常见问题解答(新手避坑指南)
Q1:为什么我的自定义标签不显示?
- 检查标签名是否包含
-(如my-component✅,mycomponent❌) - 确保
customElements.define()在使用标签之前执行 - 浏览器控制台是否有报错?
Q2:Web Components 和 React 有什么区别?
| 对比项 | Web Components | React |
|---|---|---|
| 运行时 | 无(浏览器原生) | 需要 React 库 |
| 学习成本 | 低(只需 JS + HTML) | 较高(JSX、状态管理等) |
| 性能 | 极高(直接操作 DOM) | 有虚拟 DOM 开销 |
| 生态 | 较小 | 极大 |
| 适用场景 | 轻量组件、跨框架复用 | 复杂应用、团队协作 |
💡 我建议:先学 Web Components 理解组件本质,再学 React 掌握工程化思维。
Q3:能和 React 一起用吗?
完全可以!例如:
// React 组件中使用 Web Component
function App() {
return (
<div>
<h1>React App</h1>
<my-button onclick={() => alert('来自 Web Component!')}></my-button>
</div>
);
}
只要 Web Component 已注册,React 就能把它当普通 HTML 标签用!
六、学习建议:下一步怎么走?
- 动手改造现有项目:把你常用的 UI 元素(按钮、卡片、弹窗)改成 Web Components
- 探索高级特性:
- 使用
attributeChangedCallback监听属性变化 - 通过
slot实现内容分发(类似 Vue 的插槽)
- 使用
- 结合现代工具:虽然 Web Components 无需构建,但可以用 Lit 等轻量库提升开发体验
- 性能优化实践:
- 避免在
constructor中做耗时操作 - 使用
adoptedCallback优化跨文档插入 - 懒加载大型组件
- 避免在
🌟 我当初学的时候,就是靠把博客里的“点赞按钮”改造成 Web Component,才真正理解了“组件化”的意义。
结语
Web Components 不是 React 的替代品,而是一种更底层、更通用的组件化思路。它让你回归 Web 本质,同时享受现代开发的便利。
作为从零开始的文科生,我深知“简单”有多重要。希望这篇教程能帮你迈出原生组件化的第一步。记住:所有复杂的技术,都始于一个 <hello-world>。
现在,打开你的编辑器,写一个属于你的 <my-first-component> 吧!

评论 0