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

Promise追梦人
2025-12-14 20:54
阅读 596

作者:一位从中文系转行前端的“非典型程序员”
写在前面:我当初学编程时,被 React、Vue 这些框架绕得晕头转向。后来偶然接触到 Web Components ——这个浏览器原生支持的组件化方案,才真正理解了“组件”到底是什么。今天,我想用最通俗的语言,带你从零开始认识它。


为什么我要写这篇教程?

很多初学者(包括当年的我)一上来就学 React,以为“组件”是 React 发明的。其实不是!Web Components 是浏览器原生支持的组件化标准,早在 2011 年就被提出,如今已被所有现代浏览器支持。

更酷的是:你不需要安装任何框架或工具链,只要会 HTML、CSS 和一点点 JavaScript,就能写出可复用的组件!

这篇文章不讲高深理论,只讲你能马上上手的东西。哪怕你刚学会 <div> 标签,也能跟着做出来。


一、Web Components 是什么?能用来做什么?

想象一下:你在搭乐高积木。每个积木块都有固定形状和功能,你可以把它们拼在一起,组成房子、汽车、机器人……

Web Components 就是网页世界的“乐高积木”。你可以自己做一个“按钮积木”、“卡片积木”,然后在任何网页里重复使用,而且它不会被外面的样式污染。

它和 React 有什么关系?

对比项 React Web Components
是否需要构建工具 需要(如 Vite、Webpack) 不需要,原生支持
学习成本 较高(JSX、状态管理等) 极低(只需基础 JS)
浏览器支持 需要编译成 JS 才能运行 直接在浏览器运行
跨框架兼容 只能在 React 项目中用 可在 React、Vue、Angular 甚至纯 HTML 中使用

✅ 简单说:Web Components 是“底层积木”,React 是“高级玩具套装”。先学会积木,再玩套装,你会更游刃有余。


二、环境准备:30 秒搞定开发环境

你不需要安装 Node.js、npm、Python(虽然我大学主修 Python,但这里真用不上 😄),只需要:

  1. 一台电脑
  2. 一个现代浏览器(Chrome / Edge / Firefox / Safari)
  3. 一个文本编辑器(推荐 VS Code,但记事本也行)

步骤:

  1. 新建一个文件夹,比如叫 my-web-component
  2. 在里面新建一个文件:index.html
  3. 用浏览器打开它 —— 完成!

💡 提示:我当初以为前端开发必须装一堆东西,结果发现写 Web Components 就像写作文一样简单。


三、核心概念:三个“魔法”拼出组件

Web Components 由三个核心技术组成,缺一不可:

1. Custom Elements(自定义元素)

让你能创造自己的 HTML 标签,比如 <my-button><user-card>

<!-- 以前只能这样写 -->
<button class="btn btn-primary">点击</br>

<!-- 现在可以这样写 -->
<my-button>点击</my-button>

2. Shadow DOM(影子 DOM)

给你的组件一个“私人空间”,外面的 CSS 影响不到它,它的 CSS 也不会污染外面。

🌰 举个例子:你给 <my-button> 设置了红色背景,但页面其他按钮还是蓝色 —— 因为它在“影子世界”里。

3. HTML Templates(模板)

<template> 标签预定义 HTML 结构,等要用的时候再“克隆”出来,提高性能。


四、实战:手把手做一个“用户信息卡片”

我们来做一个 <user-card> 组件,显示用户头像、姓名和简介。

第一步:创建基础 HTML 文件

<!-- index.html -->
<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <title>我的第一个 Web Component</title>
</head>
<body>
  <!-- 使用自定义组件 -->
  <user-card 
    avatar="https://via.placeholder.com/50" 
    name="小明" 
    bio="前端新手,正在学习 Web Components">
  </user-card>

  <script src="user-card.js"></script>
</body>
</html>

第二步:编写组件逻辑(user-card.js)

// user-card.js

// 1. 定义模板
const template = document.createElement('template');
template.innerHTML = `
  <style>
    .card {
      border: 1px solid #ccc;
      border-radius: 8px;
      padding: 16px;
      max-width: 300px;
      font-family: sans-serif;
    }
    .avatar {
      width: 50px;
      height: 50px;
      border-radius: 50%;
      vertical-align: middle;
      margin-right: 10px;
    }
    .name {
      font-weight: bold;
      font-size: 18px;
    }
  </style>
  <div class="card">
    <img class="avatar" src="" alt="头像">
    <span class="name"></span>
    <p class="bio"></p>
  </div>
`;

// 2. 定义组件类
class UserCard extends HTMLElement {
  constructor() {
    super();
    
    // 创建 Shadow DOM
    this.attachShadow({ mode: 'open' });
    
    // 克隆模板内容到 Shadow DOM
    this.shadowRoot.appendChild(template.content.cloneNode(true));
  }

  // 当组件被插入页面时调用
  connectedCallback() {
    // 从属性获取数据
    const avatar = this.getAttribute('avatar') || '';
    const name = this.getAttribute('name') || '匿名';
    const bio = this.getAttribute('bio') || '';

    // 更新 Shadow DOM 中的内容
    this.shadowRoot.querySelector('.avatar').src = avatar;
    this.shadowRoot.querySelector('.name').textContent = name;
    this.shadowRoot.querySelector('.bio').textContent = bio;
  }
}

// 3. 注册自定义元素
customElements.define('user-card', UserCard);

第三步:在浏览器中打开 index.html

你会看到一个漂亮的用户卡片!而且:

  • 即使你在页面加 <style> body { color: red; }</style>,卡片内的文字颜色也不会变红(因为 Shadow DOM 隔离了样式)
  • 你可以多次使用 <user-card>,每个都独立工作

✅ 恭喜!你已经掌握了 Web Components 的核心!


五、进阶:让组件更智能(响应属性变化)

现在的问题是:如果动态修改 name 属性,卡片不会自动更新。

解决方案:监听属性变化

修改 UserCard 类,添加以下代码:

// 告诉浏览器:我关心哪些属性
static get observedAttributes() {
  return ['avatar', 'name', 'bio'];
}

// 当 observedAttributes 中的属性变化时触发
attributeChangedCallback(name, oldValue, newValue) {
  if (oldValue === newValue) return;
  
  // 更新对应元素
  if (name === 'avatar') {
    this.shadowRoot.querySelector('.avatar').src = newValue;
  } else if (name === 'name') {
    this.shadowRoot.querySelector('.name').textContent = newValue;
  } else if (name === 'bio') {
    this.shadowRoot.querySelector('.bio').textContent = newValue;
  }
}

现在试试这段 JS:

<script>
  setTimeout(() => {
    document.querySelector('user-card').setAttribute('name', '小红');
  }, 2000);
</script>

2 秒后,名字会自动变成“小红”!


六、常见问题解答(FAQ)

Q1:Web Components 和 React 组件能一起用吗?

完全可以! 事实上,很多大厂(如 GitHub、Salesforce)在 React 项目中嵌入 Web Components 实现跨团队共享。

// 在 React 中使用
function App() {
  return (
    <div>
      <h1>React 页面</h1>
      {/* 直接使用自定义元素 */}
      <user-card avatar="..." name="来自 React" />
    </div>
  );
}

Q2:需要学 Python 吗?

完全不需要! Web Components 只依赖浏览器原生能力。Python 是后端语言,和前端组件无关(除非你要写后端 API,那是另一回事)。

📌 技术分享小贴士:别被“全栈”吓到,先专注前端,一步步来。

Q3:浏览器兼容性如何?

浏览器 支持情况
Chrome ✅ 从 v54 起支持(2016年)
Firefox ✅ 从 v63 起支持
Safari ✅ 从 v10.1 起支持
Edge ✅ 从 Chromium 版本起支持

💡 如果要支持老浏览器(如 IE),可以用 @webcomponents/webcomponentsjs 垫片,但强烈建议放弃 IE

Q4:为什么我的样式没生效?

检查是否把 CSS 写在了 <style> 标签里,并且放在 template.innerHTML 中。Shadow DOM 外的样式无法穿透进来


七、学习建议与下一步

✅ 你现在应该掌握:

  • 如何定义自定义元素
  • 如何使用 Shadow DOM 隔离样式
  • 如何通过属性传递数据
  • 如何监听属性变化

🔜 下一步可以学:

  1. 使用 <slot> 实现内容分发(类似 React 的 children

    <!-- 组件内部 -->
    <div class="card">
      <slot name="header"></slot>
      <slot></slot> <!-- 默认插槽 -->
    </div>
    
    <!-- 使用时 -->
    <user-card>
      <h2 slot="header">我是标题</h2>
      <p>这是自定义内容</p>
    </user-card>
    
  2. 封装成可发布的组件库

    • .js 文件上传到 CDN
    • 其他项目只需 <script src="..."> 即可使用
  3. 对比学习 React 组件

    • 你会发现:React 的 props ≈ Web Components 的 attributes
    • React 的 JSX ≈ Web Components 的 template

🚫 避坑指南:

  • 不要在 constructor 里操作 DOM(此时元素还未挂载)
  • 属性名建议用 小写+中划线(如 user-name),因为 HTML 属性不区分大小写
  • Shadow DOM 的 mode: 'open' 允许外部访问(element.shadowRoot),'closed' 则完全封闭(一般用 'open' 即可)

结语:原生的力量

我从文科生转码的经历告诉我:技术没有那么神秘。Web Components 之所以让我着迷,是因为它回归了 Web 的本质 —— 开放、简单、无需依赖。

你不需要成为“框架专家”才能写组件。从今天开始,用浏览器原生能力,打造属于你自己的“乐高积木”吧!

最后送你一句话:“最好的框架,是你理解的那一个。”


技术分享不易,如果你觉得有帮助,欢迎点赞、转发,或者在评论区告诉我你想学什么!

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝