Web Components:原生组件化开发新趋势
开篇:什么是Web Components?

你是否曾经想过,在不使用任何框架(比如React、Vue)的情况下,也能写出结构清晰、可复用性强的前端代码?有没有一种方式可以让你像搭积木一样构建网页?答案就是——Web Components!
简单来说,Web Components 是浏览器原生支持的一套技术标准,它允许我们创建自定义的 HTML 标签。这些标签就像是网页中的“模块”或“积木”,我们可以把功能和样式都封装起来,随时在不同的项目中重复使用。
比如,你可以创建一个这样的按钮:
<my-fancy-button>点击我</my-fancy-button>
这并不是普通的HTML标签,而是你自己定义的组件。无论在哪里使用,这个按钮的行为和样式都是一样的,而且不需要引入额外的框架库。
为什么要学 Web Components?
- 轻量级:没有依赖,原生支持
- 可跨项目复用:一个组件写好,到处都能用
- 与框架兼容:即使你在用React/Vue/Angular,也可以集成Web Components
- 学习成本低:只需掌握基本的HTML/CSS/JavaScript就能上手
接下来,我们就来一起从零开始,打造属于你的第一个 Web Components!
环境准备:搭建我们的开发环境

学习Web Components,并不需要复杂的开发工具。只需要准备好下面这几样基础软件:
第一步:安装文本编辑器
推荐新手使用以下两种之一:
- VSCode(免费):最流行的前端开发工具,插件丰富,对初学者非常友好。
- CodeSandbox(在线):无需下载,直接在浏览器里写代码,适合快速上手实验。
👉 如果你是完全的新手,建议先使用 CodeSandbox(https://codesandbox.io/),注册一个账号后就可以直接开始写代码了。
第二步:搭建一个简单的测试页面
如果你使用 VSCode,可以这样操作:
- 创建一个文件夹,例如
web-components-tutorial - 在该文件夹内新建三个文件:
index.html(主页面)style.css(样式文件)main.js(脚本文件)
文件结构如下:
web-components-tutorial/
├── index.html
├── style.css
└── main.js
index.html 的内容如下:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<title>我的第一个Web Component</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<!-- 我们的组件将在这里展示 -->
<script src="main.js"></script>
</body>
</html>
现在你已经准备好开发环境啦!
核心概念:揭开 Web Components 的神秘面纱

Web Components 其实是由三项核心技术组成的:
| 技术名称 | 中文翻译 | 功能说明 |
|---|---|---|
| Custom Elements | 自定义元素 | 让我们创建自己的 HTML 标签 |
| Shadow DOM | 影子文档对象模型 | 将样式和HTML隔离,防止污染 |
| HTML Templates | HTML 模板 | 提前写好但不显示的HTML结构 |
接下来我们将逐一讲解这些概念,并配合代码实例理解。
一、Custom Elements:创建你自己的 HTML 标签
这是 Web Components 最核心的部分。你可以通过 JavaScript 定义一个新的 HTML 元素,比如 <hello-world>。
示例代码
// main.js
class HelloWorld extends HTMLElement {
constructor() {
super();
this.innerHTML = "你好,世界!";
}
}
customElements.define("hello-world", HelloWorld);
<!-- index.html -->
<body>
<hello-world></hello-world>
</body>
刷新页面后,你会看到屏幕上显示:“你好,世界!”
是不是很神奇?这就是你的第一个自定义组件!
二、Shadow DOM:给组件穿“防护服”
如果我们直接在组件中插入 HTML 和 CSS,可能会跟外部样式冲突。这时就需要用到 Shadow DOM。
示例代码
// main.js
class FancyButton extends HTMLElement {
constructor() {
super();
// 创建一个shadow root
const shadow = this.attachShadow({ mode: "open" });
// 创建按钮元素
const button = document.createElement("button");
button.textContent = "点击试试";
// 创建样式
const style = document.createElement("style");
style.textContent = `
button {
background-color: lightblue;
padding: 10px 20px;
border-radius: 5px;
color: #333;
font-size: 16px;
cursor: pointer;
}
`;
// 插入样式和按钮
shadow.appendChild(style);
shadow.appendChild(button);
}
}
customElements.define("fancy-button", FancyButton);
<!-- index.html -->
<body>
<fancy-button></fancy-button>
</body>
你会发现这个按钮有专属的样式,不会影响页面上其他普通按钮的样式。这就是 Shadow DOM 带来的样式隔离效果。
三、HTML Templates:提前写好模板内容
有时候我们不想每次都在JavaScript里拼接HTML字符串,这时可以用 HTML 的 <template> 标签。
<!-- index.html -->
<template id="card-template">
<style>
.card {
border: 1px solid #ccc;
padding: 16px;
width: 200px;
margin: 16px auto;
box-shadow: 2px 2px 6px rgba(0,0,0,0.2);
}
</style>
<div class="card">
<h3>产品信息卡</h3>
<p>这是一个基于模板的内容卡片</p>
</div>
</template>
<script type="module" src="main.js"></script>
// main.js
class ProductCard extends HTMLElement {
constructor() {
super();
const template = document.getElementById("card-template");
const content = template.content.cloneNode(true);
const shadow = this.attachShadow({ mode: "open" });
shadow.appendChild(content);
}
}
customElements.define("product-card", ProductCard);
<!-- 页面使用 -->
<body>
<product-card></product-card>
</body>
这个例子中,我们通过 <template> 预设了一个产品的信息卡片样式和结构,在组件中调用并渲染。这种方式更清晰、易维护。
实战项目:做一个带交互的待办事项组件
前面我们介绍了基础知识,现在来实战做一个带交互的小型组件——todo-item,它可以显示任务内容,并能点击删除。
Step 1:HTML 结构
<!-- index.html -->
<body>
<div id="app">
<h1>我的任务清单</h1>
<todo-item text="完成作业"></todo-item>
<todo-item text="买菜"></todo-item>
</div>
</body>
Step 2:JavaScript 实现组件逻辑
// main.js
class TodoItem extends HTMLElement {
constructor() {
super();
// 获取传入的属性
const text = this.getAttribute("text") || "默认任务";
// 创建 shadow DOM
const shadow = this.attachShadow({ mode: "open" });
// 构建模板内容
const template = document.createElement("template");
template.innerHTML = `
<style>
.item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
border: 1px solid #ddd;
margin-bottom: 5px;
border-radius: 4px;
background-color: #fafafa;
}
.delete-btn {
background: red;
color: white;
border: none;
padding: 4px 8px;
cursor: pointer;
border-radius: 4px;
}
</style>
<div class="item">
<span>${text}</span>
<button class="delete-btn">X</button>
</div>
`;
// 添加内容到 shadow DOM
const clone = template.content.cloneNode(true);
shadow.appendChild(clone);
// 删除按钮事件绑定
const deleteBtn = shadow.querySelector(".delete-btn");
deleteBtn.addEventListener("click", () => {
this.remove(); // 移除当前 todo-item 组件
});
}
}
customElements.define("todo-item", TodoItem);
运行效果:
- 页面会显示两个任务项
- 点击 “X” 可以删除对应条目
🎉 这就是一个功能完整的 Web Components!
常见问题解答
问题1:为什么我的自定义组件不生效?
常见原因:
- 没有正确注册组件:确保调用了
customElements.define()。 - 标签名必须有连字符(如:
my-component):不能使用单个单词。 - 脚本加载顺序错误:要确保 JS 执行在 HTML 使用之前。
✅ 解决办法:检查是否有语法错误、命名是否规范、JS 加载顺序是否正确。
问题2:Shadow DOM 中的样式会不会影响外面?
不会!Shadow DOM 的最大好处是 样式隔离。也就是说,你在组件内部写的样式只作用于组件本身,不会“跑出去”干扰别的元素。
问题3:能不能动态设置属性?
当然可以。可以通过属性监听 (attributeChangedCallback) 来实现。
示例:
class MyComponent extends HTMLElement {
static get observedAttributes() {
return ["color"];
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === "color") {
const shadow = this.attachShadow({ mode: "open" });
const div = document.createElement("div");
div.style.color = newValue;
div.textContent = "颜色改变了:" + newValue;
shadow.appendChild(div);
}
}
}
<my-component color="red"></my-component>
学习建议:下一步怎么学?
恭喜你完成了这篇入门教程!接下来你可以继续深入以下几个方向:
1. 学习高级特性
- 使用
slot插槽机制,提升组件灵活性 - 使用
property reflection实现属性同步 - 支持生命周期方法(connectedCallback、disconnectedCallback)
2. 开发更多实用组件
尝试做这些组件练手:
- 卡片组件(图片+标题+描述)
- 输入框验证组件
- 分页组件
- 导航栏菜单组件
3. 整合现有项目
尝试将你写好的 Web Components 应用到 Vue 或 React 项目中,观察如何共存和通信。
4. 推荐学习资源
- MDN 官方文档:https://developer.mozilla.org/zh-CN/docs/Web/Web_Components
- W3C 官方标准文档
- CodePen 或 CodeSandbox 上搜索 “web components” 查看案例
结语
Web Components 正在成为现代前端开发的重要组成部分。它不仅能够帮助我们构建结构清晰、易于维护的组件,还能让我们摆脱对第三方框架的过度依赖。
通过本文的学习,相信你已经掌握了它的基本概念与使用方法。从今天起,试着去设计属于你自己的组件吧,让每一个功能块都像“积木”一样灵活可用!
继续加油,未来的大前端工程师就在你脚下!✨

评论 0