Web Components:前端新人也能玩转原生组件化

资深-张娟_极客-工程师
2026-03-18 16:26
阅读 2629

大家好,我是公司前端团队的培训负责人,过去五年带过近百名应届生。最近在给新人做技术选型分享时,发现很多人一听到“组件化”就立刻想到 React、Vue。其实,浏览器早就原生支持组件化开发了——它叫 Web Components。今天我就用最接地气的方式,带零基础的同学从零开始掌握这项被低估但极其重要的技术。

我当初学的时候,也以为 Web Components 是“老古董”,直到在一个微前端项目中用它解决了跨框架复用难题,才真正体会到它的威力。


为什么我们要学 Web Components?

简单说:它是浏览器自带的组件系统,不依赖任何框架

  • 你写的组件可以在 React、Vue、Angular 甚至纯 HTML 页面里直接用。
  • 不需要打包工具(如 Webpack)也能运行。
  • 被所有现代浏览器原生支持(Chrome、Firefox、Edge、Safari 全都 OK)。

💡 小贴士:虽然 LangChain 和 MCP(Model Context Protocol)是 AI 工程领域的热门词,React 是前端主流框架,但 Web Components 提供了一种与它们互补的底层能力——真正的跨技术栈 UI 复用。比如,你可以用 Web Components 封装一个通用的聊天窗口,然后在基于 LangChain 构建的 AI 应用中嵌入它,无论前端是用 React 还是别的框架。


环境准备:5 分钟搭好开发环境

好消息是:你只需要一个浏览器和一个文本编辑器

所需工具清单

工具 版本要求 说明
浏览器 Chrome 88+ / Firefox 90+ 支持 Web Components 所有特性
编辑器 VS Code / Sublime / 记事本 任意文本编辑器均可
本地服务器(可选) Live Server 插件 或 Python http.server 避免本地文件协议限制

快速启动步骤

  1. 新建一个文件夹,比如 web-components-demo
  2. 在里面创建 index.html
  3. 如果你用 VS Code,安装 Live Server 插件,右键“Open with Live Server”
  4. 没有插件?打开终端,进入文件夹,运行:
    # Python 3 用户
    python -m http.server 8080
    # 然后访问 http://localhost:8080
    

⚠️ 注意:直接双击 HTML 文件在浏览器打开可能会因安全策略导致部分功能失效,建议始终通过本地服务器访问。


核心三剑客:Web Components 的三大支柱

Web Components 不是一个单一技术,而是由三个浏览器原生 API 组成:

1. Custom Elements(自定义元素)

让你能像 <button> 一样定义自己的标签,比如 <my-button>

class MyButton extends HTMLElement {
  constructor() {
    super();
    this.innerHTML = '<button style="color: blue;">点我</button>';
  }
}

// 注册自定义元素
customElements.define('my-button', MyButton);

在 HTML 中使用:

<my-button></my-button>

✅ 规则:自定义元素名必须包含连字符 -,比如 x-alertui-card,这是为了避免和未来 HTML 标准冲突。


2. Shadow DOM(影子 DOM)

实现样式和结构的完全隔离。你组件内部的 CSS 不会影响外部页面,外部样式也不会污染你的组件。

class IsolatedCard extends HTMLElement {
  constructor() {
    super();
    // 创建 Shadow DOM
    const shadow = this.attachShadow({ mode: 'open' });
    
    shadow.innerHTML = `
      <style>
        .card { border: 2px solid green; padding: 16px; }
        p { color: darkgreen; }
      </style>
      <div class="card">
        <p>我是隔离的卡片!</p>
      </div>
    `;
  }
}

customElements.define('isolated-card', IsolatedCard);

即使页面全局有 p { color: red; },这个卡片里的文字依然是绿色!


3. HTML Templates(模板)

<template> 标签预定义可复用的 HTML 结构,不会被渲染,直到你用 JS 把它“实例化”。

<template id="user-card-template">
  <style>
    .user { border: 1px dashed gray; margin: 8px; }
  </style>
  <div class="user">
    <h3></h3>
    <p></p>
  </div>
</template>

在 JS 中使用:

class UserCard extends HTMLElement {
  constructor() {
    super();
    const template = document.getElementById('user-card-template');
    const clone = template.content.cloneNode(true);
    clone.querySelector('h3').textContent = this.getAttribute('name');
    clone.querySelector('p').textContent = this.getAttribute('bio');
    
    const shadow = this.attachShadow({ mode: 'open' });
    shadow.appendChild(clone);
  }
}

customElements.define('user-card', UserCard);

HTML 使用:

<user-card name="小明" bio="前端新手"></user-card>

实战:做一个可复用的“通知弹窗”组件

现在,我们把上面三个技术融合起来,做一个真实的组件。

第一步:规划功能

  • 显示一条消息
  • 3 秒后自动消失
  • 可通过属性设置类型(info / warning / error)
  • 外部可通过 JS 调用显示方法

第二步:编写代码

新建 notification-toast.js

class NotificationToast extends HTMLElement {
  constructor() {
    super();
    
    // 创建 Shadow DOM
    const shadow = this.attachShadow({ mode: 'open' });
    
    // 定义模板
    shadow.innerHTML = `
      <style>
        :host {
          position: fixed;
          top: 20px;
          right: 20px;
          z-index: 1000;
          opacity: 0;
          transition: opacity 0.3s;
        }
        :host([visible]) {
          opacity: 1;
        }
        .toast {
          padding: 12px 20px;
          border-radius: 4px;
          color: white;
          font-family: sans-serif;
        }
        .info { background: #2196F3; }
        .warning { background: #FF9800; }
        .error { background: #F44336; }
      </style>
      <div class="toast"></div>
    `;
    
    this.toastDiv = shadow.querySelector('.toast');
    this.visible = false;
  }

  // 监听属性变化
  static get observedAttributes() {
    return ['type'];
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'type') {
      this.toastDiv.className = `toast ${newValue || 'info'}`;
    }
  }

  // 显示通知
  show(message, type = 'info') {
    this.setAttribute('type', type);
    this.toastDiv.textContent = message;
    this.setAttribute('visible', '');
    this.visible = true;

    // 3秒后隐藏
    setTimeout(() => {
      this.hide();
    }, 3000);
  }

  hide() {
    this.removeAttribute('visible');
    this.visible = false;
  }
}

customElements.define('notification-toast', NotificationToast);

第三步:在 HTML 中使用

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Web Components 通知组件</title>
</head>
<body>
  <button onclick="showInfo()">显示信息</button>
  <button onclick="showWarning()">显示警告</button>
  <button onclick="showError()">显示错误</button>

  <!-- 自定义组件 -->
  <notification-toast id="toast"></notification-toast>

  <script src="notification-toast.js"></script>
  <script>
    const toast = document.getElementById('toast');

    function showInfo() {
      toast.show('这是一条普通信息', 'info');
    }

    function showWarning() {
      toast.show('注意:操作不可逆!', 'warning');
    }

    function showError() {
      toast.show('网络连接失败', 'error');
    }
  </script>
</body>
</html>

现在点击按钮,就能看到不同样式的弹窗了!而且它完全独立,不会受页面其他 CSS 影响。


新手常见问题 & 避坑指南

❓ 问题1:为什么我的组件不显示?

  • 检查是否调用了 customElements.define()
  • 确保标签名包含连字符,如 my-component,不能是 mycomponent
  • 确保 JS 在 HTML 元素之后加载,或使用 DOMContentLoaded 事件

❓ 问题2:如何传递复杂数据(比如对象)?

Web Components 原生只支持字符串属性。传对象可以用:

// 设置属性(会自动转为字符串)
element.setAttribute('user', JSON.stringify({ name: '小明' }));

// 或者直接设 property(推荐)
element.user = { name: '小明' }; // 在类中定义 getter/setter

在组件类中:

set user(value) {
  this._user = value;
  this.render(); // 更新视图
}

❓ 问题3:能和 React 一起用吗?

完全可以!React 支持直接使用 Web Components:

function App() {
  return (
    <div>
      <notification-toast id="react-toast" />
      <button onClick={() => {
        document.getElementById('react-toast').show('来自 React 的消息');
      }}>
        在 React 中调用 Web Component
      </button>
    </div>
  );
}

🌟 这正是 Web Components 的核心价值:成为不同技术栈之间的“通用语言”。无论是 LangChain 构建的 AI 后台,还是 React 开发的管理界面,只要嵌入同一个 <ai-chat-widget>,就能获得一致体验。


下一步怎么学?

作为带过很多新人的老讲师,我给你三条建议:

  1. 先掌握基础三件套:Custom Elements + Shadow DOM + Template,这是地基。
  2. 尝试封装常用 UI:比如按钮、卡片、模态框,替换掉你现在项目中的重复代码。
  3. 探索高级模式
    • 使用 lit(Google 出品的轻量库)简化开发
    • 学习如何通过 slot 实现内容分发(类似 Vue 的 slot)
    • 研究如何与状态管理库结合

最后提醒:Web Components 不是要取代 React,而是补充它。当你需要跨框架复用、构建 Design System 或开发微前端子应用时,它就是你的秘密武器。


希望这篇教程能帮你打开原生组件化的大门。我在带新人时常说:“框架会变,但浏览器标准永存。” 掌握 Web Components,你就掌握了一项穿越技术周期的核心能力。

动手试试吧!写完你的第一个 <hello-world> 组件,你就已经走在了大多数前端工程师的前面。

评论 0

最热最新
暂无评论
资深-张娟_极客-工程师Lv.1
0
影响力
0
文章
0
粉丝