Web Components:不用框架也能开发组件

摘要:在现代前端开发中,组件化已经成为标准做法。大多数项目使用React、Vue等框架来构建组件,但其实浏览器本身就提供了一套创建组件的方法,这就是Web Components。

在现代前端开发中,组件化已经成为标准做法。大多数项目使用React、Vue等框架来构建组件,但其实浏览器本身就提供了一套创建组件的方法,这就是Web Components。

Web Components是一组浏览器原生API,让你能够创建自定义的HTML标签。这些标签可以在任何地方使用,不依赖任何框架,真正实现"一次编写,到处运行"。


四个核心技术

Web Components由四个关键技术组成:

  1. Custom Elements(自定义元素)
    用来定义新的HTML标签,比如<user-card>、<search-box>等。

  2. Shadow DOM(影子DOM)
    创建独立的DOM树,实现样式和结构的隔离,避免与其他元素冲突。

  3. HTML Templates(HTML模板)
    使用<template>标签预定义组件结构,需要时才渲染。

  4. Slots(插槽)
    允许在组件内部插入外部内容,实现内容分发。


动手创建一个用户卡片组件

下面我们一步步创建一个用户信息卡片组件。

第一步:注册自定义标签

首先创建一个JavaScript类来定义组件行为:

class UserCard extends HTMLElement {
  constructor() {
    super();
    // 初始化工作放在这里
  }
}

// 注册自定义标签
customElements.define('user-card', UserCard);

注意:自定义标签名必须包含短横线,这是W3C的标准要求。

第二步:创建Shadow DOM

在构造函数中创建Shadow DOM来实现样式隔离:

constructor() {
  super();
  this.shadow = this.attachShadow({ mode: 'open' });
}

mode: 'open'表示可以通过JavaScript访问Shadow DOM内部,方便调试。

第三步:定义模板结构

在HTML中定义组件模板:

<template id="user-card-template">
  <style>
    .card {
      border: 1px solid #e0e0e0;
      border-radius: 8px;
      padding: 16px;
      max-width: 300px;
      background: white;
      box-shadow: 0 2px 8px rgba(0,0,0,0.1);
      font-family: sans-serif;
    }
    .avatar {
      width: 60px;
      height: 60px;
      border-radius: 50%;
      object-fit: cover;
    }
    .name {
      font-size: 18px;
      margin: 12px 0 8px;
      color: #333;
    }
    .bio {
      color: #666;
      font-size: 14px;
      line-height: 1.4;
      margin: 0;
    }
  </style>
  
  <div class="card">
    <img class="avatar" src="" alt="用户头像">
    <h3 class="name"><slot name="name">用户名</slot></h3>
    <p class="bio"><slot>个人简介</slot></p>
  </div>
</template>

在组件类中使用这个模板:

constructor() {
  super();
  this.shadow = this.attachShadow({ mode: 'open' });
  
  const template = document.getElementById('user-card-template');
  const content = template.content.cloneNode(true);
  this.shadow.appendChild(content);
}

第四步:使用组件

现在可以在HTML中直接使用这个组件:

<user-card>
  <span slot="name">李小明</span>
  前端开发工程师,专注于用户体验和性能优化。
</user-card>

第五步:添加交互功能

让组件更智能,可以响应属性变化:

class UserCard extends HTMLElement {
  constructor() {
    super();
    this.shadow = this.attachShadow({ mode: 'open' });
    
    const template = document.getElementById('user-card-template');
    const content = template.content.cloneNode(true);
    this.shadow.appendChild(content);
  }

  // 定义要监听的属性
  static get observedAttributes() {
    return ['avatar', 'theme'];
  }

  // 属性变化时的回调
  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'avatar') {
      const avatarImg = this.shadow.querySelector('.avatar');
      avatarImg.src = newValue;
    }
    if (name === 'theme' && newValue === 'dark') {
      const card = this.shadow.querySelector('.card');
      card.style.background = '#333';
      card.style.color = 'white';
    }
  }

  // 组件插入页面时调用
  connectedCallback() {
    console.log('组件已加载');
  }

  // 组件移除时调用
  disconnectedCallback() {
    console.log('组件已移除');
  }
}

使用带属性的组件:

<user-card avatar="user.jpg" theme="dark">
  <span slot="name">张小华</span>
  全栈工程师,热爱开源技术。
</user-card>


Web Components的优势

无需依赖框架
纯原生实现,适合轻量级项目,或者不希望引入大型框架的场景。

跨框架使用
可以在React、Vue、Angular等任何框架中使用,兼容性很好。

强样式隔离
Shadow DOM确保组件样式不会影响外部,外部样式也不会影响组件内部。

高可复用性
一次开发,可以在多个项目中重复使用。

渐进式采用
可以在现有项目中逐步引入,不需要重写整个项目。


浏览器支持情况

  • Chrome:54版本以上完全支持

  • Firefox:63版本以上完全支持

  • Safari:10.1版本以上完全支持

  • Edge:79版本以上完全支持

对于旧版本浏览器,可以使用webcomponents.js这个polyfill来提供支持。


适合的使用场景

设计系统
构建企业级的设计系统,确保UI组件在不同项目中保持一致。

微前端
在微前端架构中,各个子应用可以共享基础UI组件。

第三方组件
开发需要嵌入到其他网站的组件,比如客服聊天窗口、分享按钮等。

跨团队协作
不同团队可以共享基础组件,提高开发效率。

传统项目升级
在不使用现代框架的项目中引入组件化开发。


实际开发建议

从简单组件开始
先尝试创建按钮、输入框、卡片等简单组件,熟悉基本概念。

合理使用Shadow DOM
虽然样式隔离很好,但有时候也需要让外部控制组件样式,可以通过CSS变量来实现:

:host {
  --primary-color: #007bff;
  --border-radius: 8px;
}

.card {
  background-color: var(--primary-color);
  border-radius: var(--border-radius);
}

处理事件
在组件内部处理用户交互:

connectedCallback() {
  this.shadow.querySelector('.card')
    .addEventListener('click', this.handleClick.bind(this));
}

handleClick() {
  this.dispatchEvent(new CustomEvent('card-click', {
    detail: { message: '卡片被点击了' },
    bubbles: true
  }));
}

性能考虑
对于频繁更新的组件,注意避免不必要的重渲染。


总结

Web Components不是要取代React或Vue,而是提供了一种更基础的组件化方案。它让开发者能够用Web标准技术来创建可复用的组件。

学习Web Components有助于理解前端组件化的底层原理。即使你主要使用框架开发,了解这些基础知识也能让你成为更好的开发者。

尝试把你项目中的一些通用组件用Web Components重写,体验原生组件开发的乐趣。你会发现,有时候最简单的方案就是最好的方案。


本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://shenqiku.cn/article/FLY_13072