Web Components:前端原生组件化,真的不需要框架吗?
大家好,我是你们的前端培训导师。这几年带过上百位应届生,发现很多同学一学前端就直接上手 React 或 Vue,以为“组件”是这些框架的专利。其实,浏览器早就支持原生组件化了!今天这篇教程,就是想带零基础的同学认识一个被低估的技术——Web Components。
我当初学的时候,也以为只有用 React 才能写组件。直到某次项目需要兼容老旧系统,不能引入大型框架,才第一次接触 Web Components。结果发现:它不依赖任何库,用纯 HTML、CSS 和 JavaScript 就能实现真正的组件封装!
更妙的是,即使你现在在学 React 或 Java(别急,后面会解释为什么提 Java),了解 Web Components 也能帮你理解“组件化”的本质。话不多说,咱们从零开始。
什么是 Web Components?
简单说,Web Components 是一套浏览器原生支持的技术标准,让你像搭积木一样创建可复用的自定义 HTML 元素。
比如,你可以自己定义一个 <my-button> 标签,而不是只能用 <button>。这个标签自带样式、行为和逻辑,其他开发者直接使用就行,不用关心内部怎么实现。
💡 小知识:React 是 Facebook 推出的 UI 库,靠虚拟 DOM 和 JSX 实现组件化;而 Web Components 是 W3C 标准,由浏览器直接支持,无需额外依赖。
为什么现在要学它?
- 轻量无依赖:不用打包、不用构建工具,一个 HTML 文件就能跑
- 框架无关:可以在 React、Vue、Angular 甚至纯 Java 后端渲染的页面中使用(对,Java Web 项目也能嵌入!)
- 未来趋势:越来越多原生 API 支持它,比如 Chrome 的内置组件
环境准备:三行代码就能开始
好消息:你不需要安装 Node.js、Webpack 或任何工具!
只要一个现代浏览器(Chrome、Edge、Firefox 最新版)和一个文本编辑器(记事本都行),就可以写 Web Components。
步骤如下:
- 新建一个文件,命名为
index.html - 用你喜欢的编辑器打开它
- 复制下面这段基础模板:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>我的第一个 Web Component</title>
</head>
<body>
<!-- 我们的自定义组件将在这里使用 -->
<script>
// 组件定义写在这里
</script>
</body>
</html>
✅ 避坑提示:不要在本地直接双击打开 HTML 文件(file:// 协议),某些浏览器会限制功能。建议用 VS Code 安装 Live Server 插件,或使用 Python 快速启动服务:
# 如果你有 Python 3 python -m http.server 8080
三大核心技术:Custom Elements + Shadow DOM + Template
Web Components 由三个主要技术组成,我们逐个来看。
1. 自定义元素(Custom Elements)
这是最核心的部分——让你定义自己的 HTML 标签。
// 定义一个叫 'hello-world' 的组件
class HelloWorld extends HTMLElement {
constructor() {
super(); // 必须调用父类构造函数
this.textContent = '你好,世界!';
}
}
// 注册到浏览器
customElements.define('hello-world', HelloWorld);
然后在 HTML 中使用:
<hello-world></hello-world>
刷新页面,你会看到“你好,世界!”显示出来。
⚠️ 注意:自定义元素名称必须包含连字符
-,比如my-button、user-card。这是为了避免和未来 HTML 标准冲突。
2. 模板(Template)
如果你的组件结构复杂,直接拼字符串很麻烦。这时可以用 <template> 标签预定义结构。
<template id="my-button-template">
<style>
button {
background: blue;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
}
</style>
<button>点击我</button>
</template>
然后在组件中引用它:
class MyButton extends HTMLElement {
constructor() {
super();
const template = document.getElementById('my-button-template');
const content = template.content.cloneNode(true);
this.appendChild(content);
}
}
customElements.define('my-button', MyButton);
使用方式:
<my-button></my-button>
3. 影子 DOM(Shadow DOM)
这是 Web Components 的“魔法”所在——样式隔离。
普通 HTML 中,全局 CSS 会影响所有元素。但 Shadow DOM 让组件的样式只在内部生效,不会污染外部,也不会被外部覆盖。
修改上面的 MyButton 类:
class MyButton extends HTMLElement {
constructor() {
super();
// 创建影子根节点
const shadow = this.attachShadow({ mode: 'open' });
const template = document.getElementById('my-button-template');
const content = template.content.cloneNode(true);
shadow.appendChild(content); // 注意:这里 append 到 shadow,不是 this
}
}
现在,即使你在页面里写:
button { background: red !important; }
<my-button> 里的按钮依然是蓝色!这就是 Shadow DOM 的威力。
实战:做一个可配置的用户卡片
让我们综合运用所学,做一个 <user-card name="张三" avatar="xxx.jpg"> 组件。
第一步:写模板
<template id="user-card-template">
<style>
.card {
border: 1px solid #ccc;
border-radius: 8px;
padding: 16px;
width: 200px;
font-family: Arial;
}
img {
width: 60px;
height: 60px;
border-radius: 50%;
}
</style>
<div class="card">
<img src="" alt="头像" id="avatar">
<p id="name"></p>
</div>
</template>
第二步:定义组件类
class UserCard extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
const template = document.getElementById('user-card-template');
shadow.appendChild(template.content.cloneNode(true));
// 获取内部元素引用
this.avatarImg = shadow.getElementById('avatar');
this.nameSpan = shadow.getElementById('name');
}
// 当组件属性变化时自动调用
static get observedAttributes() {
return ['name', 'avatar'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'name') {
this.nameSpan.textContent = newValue;
} else if (name === 'avatar') {
this.avatarImg.src = newValue;
}
}
}
customElements.define('user-card', UserCard);
第三步:在页面中使用
<user-card
name="李四"
avatar="https://via.placeholder.com/60">
</user-card>
<user-card
name="王五"
avatar="https://via.placeholder.com/60?text=王">
</user-card>
刷新页面,两个独立的用户卡片就出现了!而且它们的样式互不影响。
常见问题解答(新手必看)
❓ Q1:Web Components 和 React 组件有什么区别?
| 特性 | Web Components | React |
|---|---|---|
| 依赖 | 无(浏览器原生) | 需要 React 库 |
| 数据流 | 通过属性(attribute)传递 | 通过 props |
| 状态管理 | 手动实现 | useState/useReducer |
| 社区生态 | 较小 | 非常丰富 |
| 学习曲线 | 简单直接 | 需学 JSX、Hooks 等 |
💡 建议:先理解 Web Components 的原理,再学 React,你会更容易理解“为什么 React 要这么设计”。
❓ Q2:Java 后端开发能用 Web Components 吗?
当然可以!Java Web 项目(如 Spring Boot)通常返回 HTML 页面。你完全可以在 JSP、Thymeleaf 或 FreeMarker 模板中嵌入 Web Components。
例如,在 Thymeleaf 中:
<user-card
th:attr="name=${user.name}, avatar=${user.avatarUrl}">
</user-card>
后端只需提供数据,前端组件负责渲染。前后端解耦更彻底!
❓ Q3:浏览器兼容性如何?
现代浏览器(Chrome、Edge、Firefox、Safari 最新版)都已支持。旧版 IE 不支持,但如果你的项目不需要兼容 IE,完全可以放心使用。
可通过 caniuse.com 查看详细支持情况。
下一步学习建议
- 动手练习:尝试把现有项目中的按钮、弹窗改造成 Web Components
- 深入学习:
- 了解
connectedCallback/disconnectedCallback生命周期 - 学习如何派发自定义事件(
this.dispatchEvent)
- 了解
- 结合框架:研究如何在 React 中使用 Web Components(反之亦然)
- 探索工具:虽然无需构建工具,但 Lit 这类轻量库能让开发更高效
最后的话
作为带过很多新人的讲师,我真心建议:不要一上来就被框架绑架。Web Components 是理解“组件化思想”的最佳起点——它简单、透明、贴近浏览器本质。
当你能用原生技术写出可复用的组件,再去看 React 的 JSX 或 Vue 的单文件组件,就会恍然大悟:“哦,原来它们是在模拟这种能力!”
希望这篇教程能帮你打开新世界的大门。有问题欢迎留言,我会一一解答!
加油,未来的前端工程师!

评论 0