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

更新日期: 2025-10-28 阅读: 317 标签: 组件

在现代前端开发中,组件化已经成为标准做法。大多数项目使用reactvue框架来构建组件,但其实浏览器本身就提供了一套创建组件的方法,这就是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://fly63.com/article/detial/13072

相关推荐

vue重新渲染组件(重置或者更新)

当数据通过异步操作后,对之前加载的数据进行变更后,发现数据不生效。A组件或者B组件触发数据更新,C组件数据更新了,但是C组件仍显示上一次数据。

Vuetify基于vue2.0,为移动而生的组件框架

Vuetify 支持SSR(服务端渲染),SPA(单页应用程序),PWA(渐进式Web应用程序)和标准HTML页面。 Vuetify是一个渐进式的框架,试图推动前端开发发展到一个新的水平。

React高阶组件中使用React.forwardRef的技巧

之前使用React.forwardRef始终无法应用于React高阶组件中,关键点就是React.forwardRef的API中ref必须指向dom元素而不是React组件。codepen实例请划到底部。

Vue使用Props绑定Object并且传参

通过Props 给子组件传变量,变量是对象时,子组件无法在首次打开时获取到传入对象数据,并且在父组件中改变对象的属性,子组件也是无法监听

Vue中插槽的作用_Vue组件插槽的使用以及调用组件内的方法

通过给组件传递参数, 可以让组件变得更加可扩展, 组件内使用props接收参数,slot的使用就像它的名字一样, 在组件内定义一块空间。在组件外, 我们可以往插槽里填入任何元素。slot-scope的作用就是把组件内的数据带出来

React Hook父组件获取子组件的数据/函数

我们知道在react中,常用props实现子组件数据到父组件的传递,但是父组件调用子组件的功能却不常用。文档上说ref其实不是最佳的选择,但是想着偷懒不学redux,在网上找了很多教程,要不就是hook的讲的太少

使用Vue 自定义文件选择器组件

文件选择元素是web上最难看的 input 类型之一。它们在每个浏览器中实现的方式不同,而且通常非常难看。这里有一个解决办法,就是把它封装成一个组件。

element-ui 的隐藏滚动组件el-scrollbar

为什么要用el-scrollbar,大家都知道,模拟一个滚动不难,而且市面上有很多这样的库。我考虑的,首先项目用的框架是Vue,然后用的组件库是Element,Element官网也有很多滚动

vue中prop属性传值解析

prop的定义:在没有状态管理机制的时候,prop属性是组件之间主要的通信方式,prop属性其实是一个对象,在这个对象里可以定义一些数据,而这些数据可以通过父组件传递给子组件。 prop属性中可以定义属性的类型,也可以定义属性的初始值。

写一个vue组件库_跟着element学习写组件

组件以插件的形式引入使用,当然,也可以直接在页面引入组件文件,两者按需使用。通过源码可知,vue不会重复安装同一个插件。以第一次安装为准,现在,可以在代码中使用组件啦~

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!