Web Components:原生组件化开发新趋势
大家好,我是你们的老朋友,一个在大厂干了3年前端开发、业余时间在B站做技术UP主的打工人。最近在评论区和私信里,我发现越来越多刚入门的同学在问:“现在学 Vue/React 还是直接学原生?”、“Web Components 是不是要火?”、“面试会不会考这个?”
其实我当初学的时候也一样迷茫。那时候一听说“原生组件”,第一反应是:“这不是老古董吗?有框架还用它?”但后来在参与公司内部低代码平台项目时,才真正体会到 Web Components 的威力——它不依赖任何框架,却能实现真正的组件复用、跨框架兼容,甚至被 GitHub、Salesforce、IBM 等大厂广泛采用。
今天这篇教程,我就带零基础的你,从零开始搞懂 Web Components,并完成一个小项目。无论你是准备求职、刷面试题,还是想给简历加点“硬核项目”,这篇文章都能帮到你!
什么是 Web Components?
简单说,Web Components 是一套浏览器原生支持的组件化技术。你可以把它理解为“不用 React/Vue,也能写出可复用的 UI 组件”。
它由三个核心部分组成:
- Custom Elements(自定义元素):让你能定义自己的 HTML 标签,比如
<my-button>。 - Shadow DOM(影子 DOM):让组件内部的样式和结构与页面其他部分隔离,避免样式污染。
- HTML Templates(模板):提供一种声明式的方式来定义组件的结构。
✅ 好处:原生支持、无需打包、跨框架(Vue/React/Angular 都能用)、体积小、性能高。
开发环境准备(超简单!)
Web Components 是浏览器原生功能,所以你只需要:
- 一个现代浏览器(Chrome / Edge / Firefox / Safari 最新版)
- 一个文本编辑器(VS Code 推荐)
- 一个本地服务器(防止
file://协议下的 CORS 问题)
快速启动本地服务(30秒搞定)
在你的项目文件夹下,打开终端,运行以下命令之一:
| 工具 | 安装命令 | 启动命令 |
|---|---|---|
| Node.js 内置 | 无需安装 | npx serve |
| Python | 无需额外安装 | python -m http.server 8080 |
| Live Server (VS Code 插件) | 在插件市场搜 "Live Server" | 右键 HTML 文件 → Open with Live Server |
💡 我当初学的时候直接双击 HTML 文件打开,结果 Shadow DOM 不生效,折腾半天才发现是协议问题!记住:一定要用
http://或https://访问!
核心概念三步走
第一步:自定义元素(Custom Elements)
我们来创建一个最简单的组件:<hello-world>
<!-- index.html -->
<script>
class HelloWorld extends HTMLElement {
constructor() {
super();
this.innerHTML = '<p>Hello, Web Components!</p>';
}
}
// 注册自定义元素
customElements.define('hello-world', HelloWorld);
</script>
<hello-world></hello-world>
🔍 注意:
- 类名必须继承
HTMLElement- 标签名必须包含连字符
-(如my-button,不能叫button)- 必须先定义,再使用(或者用
defer脚本)
第二步:Shadow DOM(隔离样式)
上面的例子有个问题:如果页面有全局 p { color: red; },组件里的文字也会变红。怎么办?用 Shadow DOM!
class HelloWorld extends HTMLElement {
constructor() {
super();
// 创建 Shadow Root
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
p { color: blue; font-size: 20px; }
</style>
<p>Hello, Web Components!</p>
`;
}
}
customElements.define('hello-world', HelloWorld);
现在,即使外面有全局样式,也不会影响组件内部!
第三步:使用 <template> 模板(推荐写法)
为了结构更清晰,我们可以把 HTML 放进 <template>:
<template id="hello-template">
<style>
p { color: green; }
</style>
<p>Hello from template!</p>
</template>
<script>
class HelloWorld extends HTMLElement {
constructor() {
super();
const template = document.getElementById('hello-template');
const content = template.content.cloneNode(true);
this.attachShadow({ mode: 'open' }).appendChild(content);
}
}
customElements.define('hello-world', HelloWorld);
</script>
实战项目:做一个可复用的计数器组件
现在,我们来做一个带“+”、“-”按钮的计数器 <counter-widget>,并让它支持初始值配置。
步骤 1:写 HTML 结构
<!-- index.html -->
<counter-widget initial-value="5"></counter-widget>
<template id="counter-template">
<style>
.counter {
border: 1px solid #ccc;
padding: 10px;
display: inline-block;
border-radius: 4px;
}
button { margin: 0 5px; }
</style>
<div class="counter">
<button id="decrease">-</button>
<span id="value">0</span>
<button id="increase">+</button>
</div>
</template>
步骤 2:写 JavaScript 逻辑
class CounterWidget extends HTMLElement {
constructor() {
super();
const template = document.getElementById('counter-template');
const content = template.content.cloneNode(true);
this.shadow = this.attachShadow({ mode: 'open' });
this.shadow.appendChild(content);
// 获取 DOM 元素
this.valueEl = this.shadow.getElementById('value');
this.decreaseBtn = this.shadow.getElementById('decrease');
this.increaseBtn = this.shadow.getElementById('increase');
// 初始化值(从 attribute 读取)
const initialValue = parseInt(this.getAttribute('initial-value') || '0', 10);
this.count = initialValue;
this.valueEl.textContent = this.count;
// 绑定事件
this.decreaseBtn.addEventListener('click', () => this.decrease());
this.increaseBtn.addEventListener('click', () => this.increase());
}
increase() {
this.count++;
this.valueEl.textContent = this.count;
}
decrease() {
this.count--;
this.valueEl.textContent = this.count;
}
}
customElements.define('counter-widget', CounterWidget);
效果
现在你可以在任意项目中使用:
<counter-widget initial-value="10"></counter-widget>
<counter-widget></counter-widget> <!-- 默认 0 -->
而且每个计数器都是独立的!这就是组件化的魅力。
新手常见问题 & 避坑指南
❓ Q1:Web Components 和 React/Vue 组件有什么区别?
| 特性 | Web Components | React/Vue |
|---|---|---|
| 依赖 | 无(浏览器原生) | 需要框架运行时 |
| 学习成本 | 低(只需 JS+HTML) | 中高(需学框架语法) |
| 生态 | 较小 | 丰富 |
| 跨框架 | ✅ 完全支持 | ❌ 通常不行 |
| 状态管理 | 需手动实现 | 内置或生态完善 |
🎯 建议:如果你要做微前端、嵌入第三方系统、或构建 UI 库,Web Components 是绝佳选择。
❓ Q2:面试会考 Web Components 吗?
会!尤其是一线大厂的高级前端岗或基础架构岗。常见面试题:
- “如何实现一个跨框架复用的组件?”
- “Shadow DOM 的作用是什么?”
- “Custom Elements 的生命周期有哪些?”
💼 求职建议:把今天的计数器项目放到 GitHub 上,README 里写清楚“原生 Web Components 实现,无框架依赖”,这比一堆 TodoMVC 更亮眼!
❓ Q3:浏览器兼容性怎么样?
现代浏览器基本都支持(Chrome 54+, Firefox 63+, Safari 10.1+)。如果需要兼容老 IE,可以用 polyfill。
下一步学习建议
- 深入学习:了解
connectedCallback、attributeChangedCallback等生命周期。 - 结合框架:试试在 React 中使用 Web Components(注意属性传递要用
setAttribute)。 - 工具链:探索 Lit(Google 出的轻量库,简化 Web Components 开发)。
- 开源贡献:GitHub 上搜
web-components,参与一些小项目,积累实战经验。
结语
Web Components 不是“过时技术”,而是被低估的未来趋势。随着微前端、低代码、跨团队协作需求的增长,它的价值会越来越凸显。
我当初花了一周才搞明白 Shadow DOM,现在希望你用 30 分钟就能上手。赶紧动手写个组件,传到 GitHub,下次面试时自信地说:“我用原生技术做过可复用组件!”
如果你觉得这篇教程有用,欢迎去 B站 搜我的账号(ID:前端老张),我会持续更新更多硬核实战 + 求职干货。下期见!

评论 0