Web Components:原生组件化开发新趋势

刘浩宇_云计算
2025-12-19 14:12
阅读 1972

大家好,我是你们的老朋友,一个在大厂干了3年前端开发、业余时间在B站做技术UP主的打工人。最近在评论区和私信里,我发现越来越多刚入门的同学在问:“现在学 Vue/React 还是直接学原生?”、“Web Components 是不是要火?”、“面试会不会考这个?”

其实我当初学的时候也一样迷茫。那时候一听说“原生组件”,第一反应是:“这不是老古董吗?有框架还用它?”但后来在参与公司内部低代码平台项目时,才真正体会到 Web Components 的威力——它不依赖任何框架,却能实现真正的组件复用、跨框架兼容,甚至被 GitHub、Salesforce、IBM 等大厂广泛采用。

今天这篇教程,我就带零基础的你,从零开始搞懂 Web Components,并完成一个小项目。无论你是准备求职、刷面试题,还是想给简历加点“硬核项目”,这篇文章都能帮到你!


什么是 Web Components?

简单说,Web Components 是一套浏览器原生支持的组件化技术。你可以把它理解为“不用 React/Vue,也能写出可复用的 UI 组件”。

它由三个核心部分组成:

  1. Custom Elements(自定义元素):让你能定义自己的 HTML 标签,比如 <my-button>
  2. Shadow DOM(影子 DOM):让组件内部的样式和结构与页面其他部分隔离,避免样式污染。
  3. 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


下一步学习建议

  1. 深入学习:了解 connectedCallbackattributeChangedCallback 等生命周期。
  2. 结合框架:试试在 React 中使用 Web Components(注意属性传递要用 setAttribute)。
  3. 工具链:探索 Lit(Google 出的轻量库,简化 Web Components 开发)。
  4. 开源贡献:GitHub 上搜 web-components,参与一些小项目,积累实战经验。

结语

Web Components 不是“过时技术”,而是被低估的未来趋势。随着微前端、低代码、跨团队协作需求的增长,它的价值会越来越凸显。

我当初花了一周才搞明白 Shadow DOM,现在希望你用 30 分钟就能上手。赶紧动手写个组件,传到 GitHub,下次面试时自信地说:“我用原生技术做过可复用组件!”

如果你觉得这篇教程有用,欢迎去 B站 搜我的账号(ID:前端老张),我会持续更新更多硬核实战 + 求职干货。下期见!

评论 0

最热最新
暂无评论
刘浩宇_云计算Lv.1
0
影响力
0
文章
0
粉丝