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

你有没有想过,为什么现在很多网站看起来很像?比如淘宝、京东、小红书这些 App,它们的按钮、导航栏、卡片这些设计元素好像都有点相似。这是因为它们都用了一种叫做“组件”的东西来构建。
就像我们小时候搭积木一样,每个组件就是一个可以复用的小模块。你可以把它们组合起来,拼出整个页面。在网页开发中,组件化就是这种思想的体现。
而 Web Components(网页组件) 就是浏览器本身提供的一套原生机制,让我们可以在不依赖任何框架的情况下,直接创建可复用的 HTML 元素。也就是说,你可以自己定义一个像 <my-button> 这样的标签,然后像使用 <button> 那样去使用它。
为什么我们要学习 Web Components?
- 它是浏览器原生支持的技术,不需要额外安装框架
- 高度封装性,方便复用和维护
- 跨项目兼容性好,无论是 Vue、React 还是原生 JS 项目都能使用
- 未来趋势:越来越多开发者开始重视原生组件化的价值
如果你是刚刚入门前端的新手,学会 Web Components,你就学会了如何“造轮子”,这是成为高级前端工程师的重要一步!
环境准备:搭建你的第一个 Web Components 开发环境

我们不需要安装复杂的工具。只需要一个文本编辑器 + 浏览器就够了。
第一步:选择一个代码编辑器
推荐使用 VS Code(免费),功能强大,适合新手。
下载地址:https://code.visualstudio.com/
安装完成后,打开它,进入下一步:
第二步:创建项目文件夹
- 在电脑上新建一个空文件夹,例如叫
web-components-demo - 在这个文件夹里新建两个文件:
index.html:主页面文件components/my-button.js:我们将在这里写自己的组件代码
文件结构如下:
web-components-demo/
├── index.html
└── components/
└── my-button.js
第三步:配置 HTML 文件
打开 index.html,输入以下内容:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>我的第一个 Web Component</title>
<script type="module" src="components/my-button.js"></script>
</head>
<body>
<h1>Hello Web Components!</h1>
<!-- 我们将在这里使用自定义组件 -->
<my-button>点击我</my-button>
</body>
</html>
现在你已经准备好开始写组件了!
核心概念:理解 Web Components 的三个核心


Web Components 并不是一种新技术,而是由三项标准组成的:
- Custom Elements(自定义元素)
- Shadow DOM(影子 DOM)
- HTML Templates(模板)
下面我们一一解释,并给出对应的简单代码示例。
一、Custom Elements:自定义 HTML 标签
这是 Web Components 最基本的能力 —— 你可以创建一个新的 HTML 元素标签,比如 <my-button>,然后像普通 HTML 标签一样使用它。
示例:定义一个最简单的按钮组件
在 components/my-button.js 中写下这段代码:
class MyButton extends HTMLElement {
constructor() {
super(); // 必须调用 super()
// 创建一个按钮元素并插入当前自定义元素中
const btn = document.createElement('button');
btn.textContent = this.getAttribute('label') || '默认按钮';
this.appendChild(btn);
}
}
// 注册组件,名称必须带短横线 -
customElements.define('my-button', MyButton);
回到 index.html 修改使用方式:
<my-button label="我是按钮"></my-button>
打开浏览器访问 index.html 页面,你应该能看到一个按钮了!
✨提示:自定义元素名字必须包含连字符
-,如my-button,不能写成mybutton,这是为了防止和浏览器默认标签冲突。
二、Shadow DOM:隔离样式的作用域
想象一下,你写的组件里面有一个按钮 <button class="btn">,如果页面上的 CSS 把所有 .btn 改成红色,会不会影响你的组件?
为了让组件保持风格独立,我们需要用到 Shadow DOM,它可以让组件的内容和样式与页面其他部分“隔离”。
示例:为按钮添加 Shadow DOM 和内部样式
修改 my-button.js 文件内容如下:
class MyButton extends HTMLElement {
constructor() {
super();
// 创建 Shadow DOM 容器
const shadow = this.attachShadow({ mode: 'open' });
// 创建按钮元素
const btn = document.createElement('button');
btn.textContent = this.getAttribute('label') || '默认按钮';
// 创建样式
const style = document.createElement('style');
style.textContent = `
button {
background-color: #09f;
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #06c;
}
`;
// 添加样式和按钮到 Shadow DOM 中
shadow.appendChild(style);
shadow.appendChild(btn);
}
}
customElements.define('my-button', MyButton);
刷新页面后你会发现按钮有了蓝色背景,并且即使你在外面写 .button {color: red;},也不会影响它的样式。这就实现了 样式隔离。
三、HTML Templates:更高效的组件结构定义
上面的例子我们是用 JS 动态创建按钮的。如果是更复杂的结构怎么办?一个个用 JS 创建就很麻烦了。
这时候我们可以使用 HTML Template 模板标签 <template>。
示例:使用模板创建复杂结构
先在 index.html 中添加一个模板:
<template id="my-card-template">
<style>
.card {
border: 1px solid #ddd;
padding: 10px;
border-radius: 5px;
max-width: 300px;
}
h3 {
color: #333;
}
</style>
<div class="card">
<h3>标题</h3>
<p>这是一个卡片组件,默认内容。</p>
</div>
</template>

<!-- 使用组件的地方 -->
<my-card title="我的卡片" content="这是一段新内容!"></my-card>
接下来,在 components/my-card.js 中写入:
class MyCard extends HTMLElement {
constructor() {
super();
const template = document.getElementById('my-card-template');
const shadow = this.attachShadow({ mode: 'open' });
// 复制模板内容并插入到组件中
const instance = template.content.cloneNode(true);
shadow.appendChild(instance);
// 获取并设置标题和内容
const titleEl = shadow.querySelector('h3');
const contentEl = shadow.querySelector('p');
titleEl.textContent = this.getAttribute('title') || '标题';
contentEl.textContent = this.getAttribute('content') || '默认内容';
}
}
customElements.define('my-card', MyCard);
别忘了在 index.html 中引入:
<script type="module" src="components/my-card.js"></script>
刷新页面后就能看到漂亮的卡片组件啦!
实战项目:做一个天气组件

我们来实战一个完整的例子 —— 实现一个天气信息卡片组件,使用真实 API 展示天气数据。
项目目标:
- 自定义组件标签:
<weather-card> - 接受城市名作为属性
- 调用天气 API 显示气温等信息
- 使用 Shadow DOM 和模板来组织结构
步骤一:获取天气 API 数据
我们使用公开测试 API:https://api.weatherapi.com/v1/current.json?key=YOUR_KEY&q=北京
注册账号后获取 Key 替换 YOUR_KEY,即可使用。
但为了方便演示,我们在本例中使用 mocky.io 提供的假数据接口:
https://run.mocky.io/v3/d27783c1-c7e2-4b5a-b92c-4d852869e4aa
返回格式如下:
{
"city": "Beijing",
"temp_c": 25,
"condition": "Sunny"
}
步骤二:编写组件模板
在 index.html 中添加模板:
<template id="weather-card-template">
<style>
.card {
border: 1px solid #ccc;
padding: 15px;
width: 250px;
text-align: center;
font-family: sans-serif;
}
.temp {
font-size: 2rem;
margin-top: 10px;
}
.condition {
font-style: italic;
color: #888;
}
</style>
<div class="card">
<h3 class="city">城市</h3>
<div class="temp">--°C</div>
<div class="condition">天气</div>
</div>
</template>
步骤三:编写组件逻辑
新建 components/weather-card.js 文件:
class WeatherCard extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
const template = document.getElementById('weather-card-template');
const clone = template.content.cloneNode(true);
shadow.appendChild(clone);
this.cityName = this.getAttribute('city');
this.cityEl = shadow.querySelector('.city');
this.tempEl = shadow.querySelector('.temp');
this.conditionEl = shadow.querySelector('.condition');
this.fetchWeather();
}
async fetchWeather() {
try {
const response = await fetch(`https://run.mocky.io/v3/d27783c1-c7e2-4b5a-b92c-4d852869e4aa`);
const data = await response.json();
this.cityEl.textContent = data.city;
this.tempEl.textContent = `${data.current.temp_c}°C`;
this.conditionEl.textContent = data.current.condition.text;
} catch (error) {
console.error('获取天气失败:', error);
}
}
}
customElements.define('weather-card', WeatherCard);
步骤四:使用组件
在 index.html 中使用组件:
<weather-card city="beijing"></weather-card>
别忘了加载脚本:
<script type="module" src="components/weather-card.js"></script>
刷新页面后就能看到实时天气卡片了!
常见问题解答
问:我写的组件怎么没显示出来?
答:检查以下几点:
- 组件类名是否继承
HTMLElement - 是否正确调用了
customElements.define()方法 - 组件名字是否带有连字符,如
<my-button>不要写成<mybutton> - 是否通过
<script type="module">正确加载 JS 文件
问:Shadow DOM 内部的样式会影响外面吗?
答:不会。Shadow DOM 是独立作用域的,所以里面的样式只对组件内部生效,不会影响外部页面。
问:能不能动态更新组件内容?
答:当然可以。只要你在组件内部保存了 DOM 元素的引用,就可以随时更新它们的内容和样式。
学习建议:接下来该学什么?
恭喜你完成了一个完整的 Web Components 项目!接下来你可以继续深入以下几个方向:
1. 学习更多关于生命周期的方法
Web Components 支持一些钩子函数,比如:
connectedCallback():当组件被插入页面时执行disconnectedCallback():当组件从页面移除时执行attributeChangedCallback():当属性改变时执行
2. 使用 npm 发布你的组件
你可以把做好的组件打包发布到 npm,让别人也能下载使用。
3. 结合现代框架使用
Web Components 可以在 Vue、React、Angular 等主流框架中自由使用,你可以尝试将它集成到大型项目中。
4. 深入学习 Lit 或 Stencil 等库
虽然原生写法已经很好用了,但如果你想获得更高效开发体验,可以考虑使用 Google 推出的 Lit,它是专门为 Web Components 构建的库。
总结
通过这篇教程,我们了解了:
- Web Components 是浏览器原生的组件化技术
- 它由 Custom Elements、Shadow DOM 和 HTML Templates 三大支柱组成
- 我们动手做了按钮组件、卡片组件和天气组件
- 解决了常见的问题,并给出了后续学习路径
现在你可以尝试自己做一个组件了!比如日历、进度条、下拉菜单等等。只要你有想法,就能把它变成真正的组件!
如果你喜欢这个教程,请关注我,我会持续分享更多前端开发的知识 😊

评论 0