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

想象一下,你正在装修一个房子,你可以直接买来墙板、门框、瓷砖这些“标准化”的部件,自己组装成一个完整的房间,而不必从头开始手工制作每一个零件。Web Components(网页组件)就是为网页开发者提供的类似“积木块”的东西。
它是浏览器内建的一套标准技术,允许我们创建可复用的自定义HTML标签,并封装它们的结构、样式和行为。换句话说,它让我们像搭积木一样搭建网页功能模块。而且,这些组件完全不依赖于某个前端框架(比如Vue、React),直接使用原生的JavaScript就可以实现。
它能解决什么问题?
- 代码重复:很多功能要反复写。
- 样式冲突:不同部分的CSS相互影响。
- 团队协作困难:每个人写的代码格式不一致。
通过Web Components,我们可以把常用功能封装成独立组件,哪里需要就引入,就像插件一样简单。
环境准备:搭建你的第一个Web Components开发环境

在开始写组件之前,我们先准备好开发所需的环境。不需要复杂的工具,只要一个文本编辑器和浏览器即可。
所需软件与工具
- 文本编辑器:推荐 VS Code,免费且功能强大。
- 浏览器:任何现代浏览器都可以,推荐 Chrome 或 Firefox。
- 本地服务器(非必须但建议):虽然可以直接打开 HTML 文件,但如果涉及到一些高级特性或加载外部资源时,最好通过本地服务器运行项目。
如何启动本地服务器?
如果你安装了 Node.js,可以使用 http-server 快速搭建:
npm install -g http-server
cd your-project-folder
http-server
然后访问 http://localhost:8080 就能看到页面。
核心概念:Web Components的三大核心技术

Web Components并不是一个单一的技术,而是一组协同工作的标准技术,主要包括:
- Custom Elements(自定义元素)
- Shadow DOM(影子DOM)
- HTML Templates(HTML 模板)
下面我们逐一介绍并配合实例讲解。
一、Custom Elements 自定义元素
这是最核心的部分:你可以自己定义一个全新的HTML标签,比如 <my-button>,并在任何地方像普通HTML标签那样使用它。
示例:创建一个简单的自定义按钮组件
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Web Component</title>
</head>
<body>
<!-- 使用自定义标签 -->
<my-button>点我试试</my-button>
<script type="module">
// 定义一个类继承 HTMLElement
class MyButton extends HTMLElement {
constructor() {
super();
// 创建一个按钮元素
const button = document.createElement('button');
button.textContent = this.textContent; // 获取标签内的文本内容
// 添加点击事件
button.addEventListener('click', () => {
alert('按钮被点击啦!');
});
// 把按钮添加到这个自定义元素内部
this.appendChild(button);
}
}
// 注册自定义元素
customElements.define('my-button', MyButton);
</script>
</body>
</html>
✨效果说明:
当你打开这个页面,会看到一个按钮文字是“点我试试”,点击后弹出提示。这就是你的第一个自定义组件!
二、Shadow DOM 影子DOM
还记得 CSS 样式经常互相干扰的问题吗?比如你在某个地方加了一个 .btn 类型的样式,结果另一个区域也受影响。
Shadow DOM 可以让你为组件创建一个隔离的“沙箱”区域,在这里面定义的样式不会对外部产生影响。
示例:给上面的按钮加上局部CSS样式
class MyButton extends HTMLElement {
constructor() {
super();
// 创建 Shadow DOM
const shadow = this.attachShadow({ mode: 'open' });
// 创建按钮和样式
const button = document.createElement('button');
button.textContent = this.textContent;
const style = document.createElement('style');
style.textContent = `
button {
background-color: lightblue;
border: none;
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
border-radius: 5px;
}
button:hover {
background-color: deepskyblue;
}
`;
button.addEventListener('click', () => {
alert('按钮被点击啦!');
});
// 把样式和按钮加入shadow dom中
shadow.appendChild(style);
shadow.appendChild(button);
}
}
customElements.define('my-button', MyButton);
现在即使页面上有其他.btn类的样式也不会影响到这个按钮。
三、HTML Templates 模板
如果每次都在 JS 中手动创建 DOM 结构,那会很麻烦。HTML Template 元素提供了一种预定义模板的方式,你可以将HTML结构提前写好,再动态插入到组件中。
示例:用 <template> 来简化代码
<template id="my-button-template">
<style>
button {
background-color: lightgreen;
border: none;
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
border-radius: 5px;
}
button:hover {
background-color: limegreen;
}
</style>
<button><slot></slot></button>
</template>
<script type="module">
class MyButton extends HTMLElement {
constructor() {
super();
const template = document.getElementById('my-button-template').content;
const shadow = this.attachShadow({ mode: 'open' });
shadow.append(template.cloneNode(true)); // 克隆模板内容并添加到shadow root
const btn = shadow.querySelector('button');
btn.addEventListener('click', () => {
alert('按钮被点击啦!');
});
}
}
customElements.define('my-button', MyButton);
</script>
这样写的好处是,HTML结构更清晰,也更容易维护。
实战项目:动手做一个计数器组件
我们已经掌握了三个核心概念,现在来实战一下,做一个带按钮的计数器组件 <counter-component>。
功能需求:
- 包含一个显示数字的区域
- 包含两个按钮:“+1” 和 “-1”
- 数字变化只会影响这个组件本身
步骤如下:
1. 编写HTML模板
<template id="counter-template">
<style>
.counter {
font-size: 24px;
margin-bottom: 10px;
}
.buttons button {
margin-right: 10px;
padding: 8px 16px;
font-size: 16px;
}
</style>
<div class="counter">当前数值:<span id="count">0</span></div>
<div class="buttons">
<button id="increment">+1</button>
<button id="decrement">-1</button>
</div>
</template>
2. 写自定义组件逻辑
class CounterComponent extends HTMLElement {
constructor() {
super();
const template = document.getElementById('counter-template').content;
const shadow = this.attachShadow({ mode: 'open' });
shadow.appendChild(template.cloneNode(true));
const countEl = shadow.getElementById('count');
let count = 0;
shadow.getElementById('increment').addEventListener('click', () => {
count++;
countEl.textContent = count;
});
shadow.getElementById('decrement').addEventListener('click', () => {
count--;
countEl.textContent = count;
});
}
}
customElements.define('counter-component', CounterComponent);
3. 在页面中使用组件
<body>
<h1>我的第一个计数器组件</h1>
<counter-component></counter-component>
<script type="module" src="./counter.js"></script>
</body>
✅效果展示
你会看到一个带有 +1 和 -1 的小计数器,点击按钮会改变中间的数字,并且不会受到页面其他样式的干扰。
常见问题解答(FAQ)
Q1:为什么我的自定义标签没有生效?
可能原因:
- 没有正确调用
customElements.define()函数; - 自定义标签名没有包含短横线(如
my-button是合法的,但buttonx不行); - 组件未挂载到页面上,或者脚本加载顺序有问题。
Q2:为什么样式不起作用?
- 如果你用了 Shadow DOM,请确保样式是写在 Shadow DOM 里的;
- 检查是否拼写错误或选择器不对。
Q3:能不能在 React/Vue 中使用 Web Components?
完全可以!Web Components 是通用的标准技术,无论是 Vue、React、jQuery 还是原生 JS,都可以使用。
例如在 Vue 中使用:
<template>
<div>
<counter-component />
</div>
</template>
<script>
export default {}
</script>
前提是你的组件已经在全局注册过。
学习建议:下一步该怎么深入学习?
恭喜你完成了第一个 Web Components 的入门项目!下面是一些继续深入的方向建议:
✅巩固基础
- 继续尝试封装常用的 UI 组件:按钮、输入框、开关等;
- 实现数据绑定与事件通信。
📌进阶方向
- 学习如何在大型项目中组织多个 Web Components;
- 研究 Lit(Lit by Google)这样的库,它简化了组件开发流程;
- 探索如何用 Web Components 构建可跨项目复用的 UI 库。
📘推荐阅读资料
- MDN Web Docs —— Web Components 教程:https://developer.mozilla.org/zh-CN/docs/Web/Web_Components
- Lit 官方文档:https://lit.dev/docs/
- Web Components Playground:https://webcomponents.dev/
总结
Web Components 是一种轻量、强大、无需依赖第三方框架的组件化方案。它结合了自定义元素、影子DOM和模板系统,能够帮助我们构建高度可复用的界面组件。
希望这篇教程能为你打开这扇“组件化之门”。记住:编程最重要的不是背知识,而是不断实践、多敲代码。接下来,不妨试着把你常用的UI片段都封装成自己的组件吧!

评论 0