Web Components 基础

2.8 Web Components 基础

Web Components 是浏览器原生支持的组件化标准体系,允许开发者创建可复用、强封装的自定义 HTML 元素,核心由 Shadow DOM、自定义元素、HTML Template 三大技术构成。

它实现了组件级的样式隔离、结构封装与原生级复用,无需依赖前端框架即可实现跨项目、跨技术栈的组件复用,是原生前端组件化的底层标准。


2.8.1 Shadow DOM 影子 DOM

Shadow DOM 是 Web Components 的核心封装机制,它可以在普通 DOM 元素内部创建一个独立、与外部文档 DOM 树隔离的影子 DOM 子树。

内部的样式、DOM 结构不会污染外部页面,外部的全局样式也不会渗透到组件内部,从浏览器原生层面解决了组件样式冲突、DOM 结构干扰的问题。

一、基础使用语法

通过元素的 attachShadow() 方法挂载影子 DOM,返回影子根节点 shadowRoot,后续所有 DOM 操作都在影子根内部完成。

javascript 复制代码
// 获取宿主元素

const host = document.querySelector('.component-host');

// 挂载影子 DOM

const shadowRoot = host.attachShadow({

  mode: 'open' // 开放模式,外部可访问影子根

});



// 向影子 DOM 中插入结构与样式

shadowRoot.innerHTML = `

  <style>

    .card { color: #1677ff; }

    /* 内部样式仅在影子 DOM 内生效,不会影响外部 */

  </style>

  <div class="card">

    <slot></slot>

  </div>

`;
二、mode 封装模式

Shadow DOM 提供两种封装模式,对应不同的外部访问权限:

|-----------------|---------------------------------------|--------------------|
| mode 取值 | 外部访问性 | 适用场景 |
| open | 外部可通过 host.shadowRoot 访问影子 DOM 内部 | 通用组件场景,便于外部交互与调试 |
| closed | 外部无法获取影子根,host.shadowRoot 返回 null | 强封装场景,禁止外部操作组件内部结构 |

三、核心特性
  1. 样式完全隔离:外部 CSS 选择器无法选中影子 DOM 内部元素,内部样式也不会泄露到外部,彻底解决组件样式污染问题。
  2. DOM 结构隔离:外部 querySelector 等 DOM 查询方法无法穿透影子 DOM,内部 DOM 独立维护。
  3. 事件重定向:内部事件冒泡到外部时,event.target 会自动指向宿主元素,隐藏内部结构细节,保持封装性。
  4. 插槽内容分发:通过 <slot> 标签接收外部传入的子内容,实现组件内容的灵活扩展。
四、slot 插槽机制

插槽是 Shadow DOM 的内容分发接口,用于承载外部传入的子元素,支持默认内容与具名插槽。

javascript 复制代码
<!-- 外部使用组件时传入内容 -->

<custom-card>

  <span slot="title">卡片标题</span>

  <p>卡片正文内容</p>

</custom-card>

// 影子 DOM 内部定义插槽位置

shadowRoot.innerHTML = `

  <div class="card">

    <h3 class="card-title">

      <slot name="title">默认标题</slot>

    </h3>

    <div class="card-body">

      <slot>默认正文内容</slot>

    </div>

  </div>

`;

2.8.2 自定义元素基础

自定义元素(Custom Elements)允许开发者定义全新的 HTML 标签,通过继承原生 HTMLElement 类实现自定义标签的行为与业务逻辑,配合 Shadow DOM 即可实现完整的原生组件,是 Web Components 的核心载体。

一、定义与注册流程
  1. 创建自定义元素类,继承 HTMLElement 基类
  2. 通过 customElements.define() 注册标签,标签名必须包含横杠,避免与原生 HTML 标签冲突
javascript 复制代码
// 定义自定义元素类

class CustomButton extends HTMLElement {

  constructor() {

    super(); // 必须先调用父类构造函数

    // 挂载 Shadow DOM

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

    // 组件结构与样式

    shadow.innerHTML = `

      <style>

        .btn {

          padding: 8px 16px;

          border: none;

          border-radius: 4px;

          background: #1677ff;

          color: #fff;

          cursor: pointer;

        }

      </style>

      <button class="btn">

        <slot></slot>

      </button>

    `;

  }



  // 元素被插入到 DOM 时触发

  connectedCallback() {

    console.log('自定义按钮已挂载到页面');

  }



  // 元素从 DOM 移除时触发

  disconnectedCallback() {

    console.log('自定义按钮已从页面卸载');

  }

}



// 注册自定义标签,标签名必须包含横杠

customElements.define('custom-button', CustomButton);

注册后即可直接在 HTML 中像原生标签一样使用:

html 复制代码
<custom-button>确认提交</custom-button>
二、核心生命周期钩子

自定义元素提供了标准化的生命周期钩子,用于管控组件不同阶段的逻辑:

|----------------------------------------------------|----------------|----------------------------|
| 生命周期方法 | 触发时机 | 典型用途 |
| constructor() | 元素实例创建时 | 初始化状态、挂载 Shadow DOM、绑定内部事件 |
| connectedCallback() | 元素被插入到 DOM 文档时 | 请求数据、添加全局监听、初始化第三方依赖 |
| disconnectedCallback() | 元素从 DOM 文档移除时 | 清理定时器、移除全局监听、销毁资源 |
| attributeChangedCallback(name, oldVal, newVal) | 监听的属性发生变化时 | 响应属性变化,同步更新组件状态 |

三、核心特点
  1. 原生级支持:W3C 标准,浏览器原生实现,无需引入任何框架或依赖库。
  2. 语义化标签:自定义标签具备业务语义,提升 HTML 源码的可读性与可维护性。
  3. 跨技术栈复用:定义后可在任意前端框架、任意页面中直接使用,兼容所有技术体系。
  4. 标准化生命周期:统一的生命周期钩子,组件逻辑管控清晰规范。
四、典型应用场景
  • 基础 UI 组件库:按钮、弹窗、表单控件等通用基础组件,实现跨框架复用。
  • 微前端场景:不同技术栈项目之间的组件共享与嵌入,避免重复开发。
  • 第三方嵌入组件:提供给外部页面嵌入的小组件,样式隔离无冲突。
  • 业务模块封装:将复杂业务模块封装为独立标签,降低系统耦合度。
相关推荐
中文呀2 分钟前
三维 GIS 离线缓存实战:逆向 CesiumOfflineCache,并亲手为它补上"自动更新"能力
前端
前进前进前进42 分钟前
TRAE 全链路实战:做了一个警示牌一键生成器:纯 Canvas、单文件、零依赖
前端
小万7422 小时前
God‘s Eye View 深度解析:15 路公开信号 + CesiumJS 的浏览器级 OSINT 控制台,60 帧渲染的“真实间谍卫星模拟器”
前端
IT星宿2 小时前
浏览器标签页全是一个图标:我用名字哈希生成 favicon,让每个项目都有身份标识
前端
OpsEye2 小时前
大模型账单对账难:不同厂商 Token 统计口径差异怎么处理
javascript·ai编程
全栈项目管理程序猿2 小时前
ArcGIS JS 基础教程(32):体元系列 - VoxelSlice 体元切片
前端·javascript
拖孩4 小时前
AI 帮我写的小程序「厨菜记」上线满月了,挣了两百块!!!
前端·后端·微信小程序
用户360055579004 小时前
Kestrel_VLLM - 13 · 为什么排除 MoE FFN Tiling 与 Tensor Core
前端
Hilaku4 小时前
AI 流式输出都用 SSE,什么时候必须换 WebSocket?
前端·javascript·程序员
奇牙coding4 小时前
Codex CLI 升级后多文件编辑静默失效:config.yaml 枚举变更排查与修复指南
java·前端·javascript·ai