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

♀张娟
2025-06-27 05:55
阅读 3017

开篇:Web Components 是什么?为什么要学它?

开篇:Web Components 是什么?为什么要学它?

你有没有想过,为什么现在很多网站看起来很像?比如淘宝、京东、小红书这些 App,它们的按钮、导航栏、卡片这些设计元素好像都有点相似。这是因为它们都用了一种叫做“组件”的东西来构建。

就像我们小时候搭积木一样,每个组件就是一个可以复用的小模块。你可以把它们组合起来,拼出整个页面。在网页开发中,组件化就是这种思想的体现。

而 Web Components(网页组件) 就是浏览器本身提供的一套原生机制,让我们可以在不依赖任何框架的情况下,直接创建可复用的 HTML 元素。也就是说,你可以自己定义一个像 <my-button> 这样的标签,然后像使用 <button> 那样去使用它。

为什么我们要学习 Web Components?

  1. 它是浏览器原生支持的技术,不需要额外安装框架
  2. 高度封装性,方便复用和维护
  3. 跨项目兼容性好,无论是 Vue、React 还是原生 JS 项目都能使用
  4. 未来趋势:越来越多开发者开始重视原生组件化的价值

如果你是刚刚入门前端的新手,学会 Web Components,你就学会了如何“造轮子”,这是成为高级前端工程师的重要一步!


环境准备:搭建你的第一个 Web Components 开发环境

环境准备:搭建你的第一个 Web Components 开发环境

我们不需要安装复杂的工具。只需要一个文本编辑器 + 浏览器就够了。

第一步:选择一个代码编辑器

推荐使用 VS Code(免费),功能强大,适合新手。
下载地址:https://code.visualstudio.com/

安装完成后,打开它,进入下一步:

第二步:创建项目文件夹

  1. 在电脑上新建一个空文件夹,例如叫 web-components-demo
  2. 在这个文件夹里新建两个文件:
    • 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 的三个核心

移动端适配方案-2

Web Components 并不是一种新技术,而是由三项标准组成的:

  1. Custom Elements(自定义元素)
  2. Shadow DOM(影子 DOM)
  3. 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>


![响应式布局概念图-1](https://code-guide.oss.shanghai.autogptai.club/common/file/download?name=date2025062705/f52dd691-d015-48c1-9e2e-218d35e05d16.jpg)


<!-- 使用组件的地方 -->
<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

最热最新
暂无评论
♀张娟Lv.1
0
影响力
0
文章
0
粉丝