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

你是否曾经在写网页的时候觉得代码越来越乱?或者想把某个功能模块反复用在多个项目中却要复制粘贴很多次?有没有一种更“聪明”的方式来组织你的代码呢?
Web Components(网页组件) 就是为了解决这些问题而诞生的技术。它是一套由 W3C(万维网联盟)制定的标准技术,允许开发者创建可复用、可封装、样式隔离的 HTML 元素。
简单来说,你可以把它想象成“自定义的 HTML 标签”。就像 <button> 或者 <input> 一样,你可以自己定义一个叫做 <my-card> 的标签,并且控制它的外观和行为。这个组件可以在任何网站上使用,而且不需要依赖特定框架(比如 Vue、React),因为它就是浏览器原生支持的技术!
这正是 Web Components 的魅力所在——它是原生的组件化解决方案。
如果你是一个刚入门前端的小白,也许你会问:“我现在学 Vue 还是 React 好吗?” 现在可以加一个选项:“顺便也学点 Web Components 吧!” 因为它不仅帮助你理解组件化的本质,还能让你写出真正“跨框架”复用的组件。
环境准备:搭建简单的开发环境

在开始学习 Web Components 之前,我们先准备好最基础的开发环境。无需安装复杂的工具,甚至不需要 Node.js,只要有个现代浏览器和一个文本编辑器就能开始。
🛠 所需工具清单:
- 浏览器:Chrome 最佳,也可以用 Firefox 或 Edge
- 文本编辑器:推荐 VS Code(完全免费)
- 文件夹结构:只需一个
index.html和一个 JS 文件即可
📁 目录结构示例:
web-components-project/
├── index.html
└── main.js
📝 步骤一:新建 index.html
打开编辑器,新建一个叫 index.html 的文件,内容如下:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>我的第一个 Web Component</title>
</head>
<body>
<h1>欢迎来到 Web Components 世界!</h1>
<!-- 我们稍后会在这里插入自定义元素 -->
<my-hello></my-hello>
<script type="module" src="./main.js"></script>
</body>
</html>
注意这里用了 <script type="module">,这是因为我们要使用 ES6 模块标准来编写组件。
📝 步骤二:创建 main.js
接着我们创建 main.js 文件:
class MyHello extends HTMLElement {
constructor() {
super();
this.innerHTML = '你好,我是 Web Component!';
}
}
customElements.define('my-hello', MyHello);
这段代码做了两件事:
- 定义了一个类
MyHello,继承自HTMLElement - 通过
customElements.define()方法将类注册为名为<my-hello>的自定义元素
▶️ 步骤三:运行一下看看效果
在本地直接打开 HTML 文件可能无法加载 JS 模块,请使用本地服务器运行。
快速启动本地服务器的方法:
- VS Code 用户可以安装插件 Live Server,右键点击
index.html选择 “Open with Live Server” - 或者使用 Python 内置服务器(命令行执行):
python3 -m http.server
然后访问 http://localhost:8000
你应该看到页面显示:
你好,我是 Web Component!
恭喜,你的第一个 Web Component 已经跑起来了!
核心概念:通俗易懂地讲解 Web Components 的关键部分

为了深入理解 Web Components,我们需要掌握三个核心接口/技术:
- Custom Elements(自定义元素)
- Shadow DOM(影子节点)
- HTML Templates(模板标签)
下面我们用小白也能听懂的语言逐个解释。
1. Custom Elements:打造自己的 HTML 标签
还记得刚才我们写的 <my-hello> 吗?这就是 Custom Element 的作用。
Custom Element 的特点:
- 可以像普通 HTML 元素那样在任意地方使用
- 可以绑定生命周期方法(如首次加载时触发某种操作)
- 支持与 JavaScript 交互
我们来看看一个完整的例子,给组件加上一些生命周期钩子函数:
class GreetingElement extends HTMLElement {
constructor() {
super();
this.innerText = '等待初始化完成...';
}
connectedCallback() {
this.innerText = '我已经被添加到页面中了!';
}
disconnectedCallback() {
console.log('我被从页面中移除了');
}
}
customElements.define('greeting-element', GreetingElement);
页面中:
<greeting-element></greeting-element>
刷新页面后,你将看到文字更新,并在控制台看到相关信息。
💡 生命周期提示:
connectedCallback:当元素被加入到文档时调用disconnectedCallback:当元素被移除时调用attributeChangedCallback:属性变化时调用adoptedCallback:用于跨文档移动时(极少使用)
2. Shadow DOM:让你的组件样式不再冲突
假设你在组件内部写了很多样式,但整个页面的 CSS 很复杂,会不会影响到你写的组件?答案是有可能会——除非你用上了 Shadow DOM!
什么是 Shadow DOM?
你可以把它理解为组件的“私人空间”,里面的内容不会受到外部样式干扰,也不会泄露出去污染全局。
来看个例子:
class PrivateCard extends HTMLElement {
constructor() {
super();
// 创建 Shadow DOM 并附加到当前元素
const shadow = this.attachShadow({ mode: 'open' });
const div = document.createElement('div');
div.textContent = '这是私密区域的内容';
div.style.color = 'red';
// 把 div 添加进 Shadow DOM 中
shadow.appendChild(div);
}
}
customElements.define('private-card', PrivateCard);
页面中:
<private-card></private-card>
现在即使你在全局写了 div { color: blue; },里面的文字仍然是红色的!
✅ Shadow DOM 的优势:
- 风格封闭,避免样式污染
- 更好的封装性和安全性
- 更清晰的层级结构
3. HTML Templates:提前定义好组件结构
如果组件内容比较复杂,每次都用 JavaScript 动态创建就很麻烦。我们可以使用 <template> 标签来预先定义组件的 HTML 结构。
<template id="user-card-template">
<style>
.card {
border: 1px solid #ccc;
padding: 1em;
border-radius: 5px;
width: 200px;
}
.name {
font-weight: bold;
}
</style>
<div class="card">
<div class="name">用户名:张三</div>
<div class="age">年龄:25</div>
</div>
</template>
对应的 JavaScript:
class UserCard extends HTMLElement {
constructor() {
super();
const template = document.getElementById('user-card-template').content;
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.appendChild(template.cloneNode(true));
}
}
customElements.define('user-card', UserCard);
页面中使用:
<user-card></user-card>
这样就生成了一个带样式的卡片组件!
实战项目:做一个小应用 —— 计数器组件
我们现在来动手做一个完整的组件——计数器(Counter)。它包含两个按钮:增加和减少,中间显示当前数值。
✅ 第一步:写模板结构和样式
在 index.html 中加入以下 <template>:
<template id="counter-component">
<style>
.container {
display: flex;
align-items: center;
justify-content: space-around;
padding: 1em;
border: 1px solid #ddd;
border-radius: 4px;
width: 200px;
margin: 1em auto;
}
button {
padding: 0.5em 1em;
cursor: pointer;
}
.count {
font-size: 1.5em;
font-weight: bold;
}
</style>
<div class="container">
<button id="minus">-</button>
<div class="count">0</div>
<button id="plus">+</button>
</div>
</template>
✅ 第二步:实现逻辑行为
我们在 main.js 中写组件类:
class CounterComponent extends HTMLElement {
constructor() {
super();
const template = document.getElementById('counter-component').content;
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.appendChild(template.cloneNode(true));
this.countEl = shadowRoot.querySelector('.count');
this.minusBtn = shadowRoot.querySelector('#minus');
this.plusBtn = shadowRoot.querySelector('#plus');
this.count = 0;
this.minusBtn.addEventListener('click', () => {
this.count--;
this.updateCount();
});
this.plusBtn.addEventListener('click', () => {
this.count++;
this.updateCount();
});
}
updateCount() {
this.countEl.textContent = this.count;
}
}
customElements.define('counter-component', CounterComponent);
✅ 第三步:使用组件
在页面中使用:
<counter-component></counter-component>
<counter-component></counter-component>
你会发现有两个独立的计数器,互不干扰,因为它们各自有独立的 Shadow DOM。
🎉 恭喜!你已经成功实现了自己的可复用组件!
常见问题解答:新手常遇到的问题 & 解决方案

❓ Q1:为什么不能直接引入普通的 <script> 脚本?
👉 A:Web Components 使用的是 ES Module 模块系统,需要设置 <script type="module">,否则 customElements.define() 会报错。
❓ Q2:如何给组件传参?
👉 A:可以通过自定义属性 + attributeChangedCallback 来处理。例如:
// 在类中定义静态属性
static get observedAttributes() {
return ['start'];
}
// 当属性变化时自动调用
attributeChangedCallback(name, oldVal, newVal) {
if (name === 'start') {
this.count = parseInt(newVal);
this.updateCount();
}
}
HTML 使用:
<counter-component start="100"></counter-component>
❓ Q3:能不能在组件内部用 jQuery 或其他库?
👉 A:当然可以!但不太推荐。Web Components 设计初衷就是轻量级和无框架依赖,如果你非要用第三方库,建议只在主项目中使用,组件尽量保持纯原生。
❓ Q4:组件间怎么通信?
👉 A:可以使用事件机制。比如:
this.dispatchEvent(new CustomEvent('change', { detail: this.count }));
页面监听:
<counter-component id="c1"></counter-component>
<script>
document.getElementById('c1').addEventListener('change', e => {
console.log('当前值:', e.detail);
});
</script>
学习建议:下一步该往哪里走?
你现在掌握了 Web Components 的基本知识并做出了一个可用的组件。接下来可以根据兴趣继续深入:
✅ 推荐学习方向:
- 深入了解 Shadow DOM 的更多用法
- 使用 Webpack / Rollup 构建大型组件库
- 结合 Lit、StencilJS 等框架提高效率
- 尝试发布自己的组件到 npm
- 对比 Web Components 与 Vue/React 的差异与协作
总结
本教程从零开始介绍了 Web Components 这项现代化的网页开发技术。我们不仅学会了什么是组件、怎样搭建环境、实现了一个计数器组件,还回答了初学者常见的疑惑。相信现在你已经对这项技术有了全面的理解。
记住一句话:组件化不是某一个框架的专利,而是网页发展的大趋势。
无论你将来是否使用 Vue、React、Angular,还是原生 JavaScript,Web Components 都是你值得掌握的一项技能。
如果你喜欢这篇文章,欢迎留言告诉我你喜欢的地方,或者你还想学哪方面的内容 😊

评论 0