当前位置: 首页 > news >正文

10.2Web Component

本文参考MDN

https://developer.mozilla.org/zh-CN/docs/Web/API/Web_components

Web Component 的提出

Web Component 的概念最早可以追溯到 2011 年,由 Google 的工程师提出。其核心规范在随后的几年中逐步发展和完善。

截至当前日期(2025年10月24日),Web Component 的核心 API 已经得到了所有现代主流浏览器的广泛支持

  • 核心 API 支持

    • Custom Elements (自定义元素):V1 版本已被 Chrome、Firefox、Safari、Edge 等现代浏览器完全支持。
    • Shadow DOM (影子 DOM):V1 版本同样被所有现代浏览器支持,提供了强大的样式和 DOM 封装能力。
    • HTML Templates (<template> 和 <slot>):这些标签也被所有现代浏览器支持,是构建可复用组件模板的基础。

Web Component 的四大核心技术

Web Component 并非单一技术,而是由四个关键的 Web API 组成:

1. 自定义元素 (Custom Elements)

这是 Web Component 的基础,它让你可以定义和注册全新的 HTML 标签。

  • customElements.define():用于注册一个自定义元素。
  • 必须包含一个连字符 -(例如 my-buttonuser-card),这是为了避免与未来的 HTML 标准元素冲突。
  • 基于 ES6 的 class 语法继承 HTMLElement 或其子类。

示例:

// 定义一个类
class MyButton extends HTMLElement {constructor() {super();// 创建 Shadow DOMthis.attachShadow({ mode: 'open' });// 设置内部结构和样式this.shadowRoot.innerHTML = `<style>button {background-color: #007bff;color: white;border: none;padding: 10px 20px;border-radius: 5px;cursor: pointer;}button:hover {background-color: #0056b3;}</style><button><slot></slot></button>`;}// 生命周期钩子:元素被添加到文档时调用connectedCallback() {console.log('MyButton 已连接到页面');}// 生命周期钩子:元素从文档中移除时调用disconnectedCallback() {console.log('MyButton 已从页面移除');}// 监听属性变化static get observedAttributes() {return ['disabled'];}attributeChangedCallback(name, oldValue, newValue) {if (name === 'disabled') {const button = this.shadowRoot.querySelector('button');button.disabled = newValue !== null;}}
}// 注册自定义元素
customElements.define('my-button', MyButton);

使用方式:

<my-button disabled>点击我</my-button>
2. 影子 DOM (Shadow DOM)

这是实现封装性的核心技术。影子 DOM 允许你将一个隐藏的、独立的 DOM 树附加到一个元素上,这个树与主文档的 DOM 是隔离的。

  • 作用域隔离:影子 DOM 内部的 CSS 样式不会影响外部页面,外部的样式也不会穿透进来(除非使用 ::part 或 ::slotted())。
  • DOM 隔离:影子 DOM 内部的元素无法被外部的 JavaScript 直接访问(除非通过 shadowRoot)。
  • 模式 (mode)
    • open:可以通过 element.shadowRoot 访问。
    • closed:无法通过 JavaScript 访问(实际应用中很少用,因为有绕过的方法)。
3. HTML 模板 (<template> 和 <slot>)
  • <template>:用于声明一段在页面加载时不会渲染的 HTML 模板。你可以克隆这段模板并插入到 DOM 中。非常适合存放组件的结构。
  • <slot>:插槽机制,允许你在自定义元素的内部预留位置,让用户传入内容进行填充。实现了内容分发。

示例模板:

<template id="user-card-template"><style>.card { border: 1px solid #ccc; padding: 16px; }.name { font-weight: bold; }</style><div class="card"><div class="name"><slot name="name">默认姓名</slot></div><div class="email"><slot name="email">默认邮箱</slot></div></div>
</template>

在 JS 中克隆模板并插入影子 DOM 即可。

4. 生命周期回调 (Lifecycle Callbacks)

自定义元素类中可以定义一些特殊的回调函数,在元素生命周期的不同阶段自动执行:

  • connectedCallback():元素被插入到 DOM 时调用。
  • disconnectedCallback():元素从 DOM 中移除时调用。
  • adoptedCallback():元素被移动到新文档时调用(如 iframe)。
  • attributeChangedCallback(attrName, oldVal, newVal):当观察的属性发生变化时调用。需要配合 observedAttributes 静态 getter 使用。

完整例子

 准备工作

你只需要一个文本编辑器(如 VS Code)和一个现代浏览器(Chrome/Firefox/Safari/Edge)。

创建以下文件结构:

web-component-demo/
├── index.html
└── fancy-button.js

📚 案例:创建一个“炫酷按钮”组件 <fancy-button>

我们来创建一个带有渐变背景、圆角、悬停动画的按钮,它支持自定义颜色,并且可以像普通按钮一样插入文字。


第一步:创建 JavaScript 文件 (fancy-button.js)

这是组件的核心逻辑。

// fancy-button.js// 1. 定义一个类,继承自 HTMLElement
class FancyButton extends HTMLElement {// 构造函数:组件创建时调用constructor() {super(); // 必须调用 super()// 2. 创建 Shadow DOM 并附加到当前元素// mode: 'open' 表示可以通过 JS 访问 shadowRootthis.attachShadow({ mode: 'open' });// 3. 定义组件的内部结构(HTML)和样式(CSS)// 我们使用模板字符串来写this.shadowRoot.innerHTML = `<style>/* 这里的样式只作用于组件内部,不会影响页面其他部分 */.button {background: linear-gradient(45deg, #667eea 0%, #764ba2 100%);color: white;border: none;padding: 12px 24px;font-size: 16px;border-radius: 25px;cursor: pointer;box-shadow: 0 4px 15px rgba(0,0,0,0.2);transition: all 0.3s ease;outline: none;}.button:hover {transform: translateY(-2px);box-shadow: 0 7px 20px rgba(0,0,0,0.3);}.button:active {transform: translateY(0);}/* 如果组件有 primary 属性,应用主色调 */:host([primary]) .button {background: linear-gradient(45deg, #f093fb 0%, #f5576c 100%);}/* 如果组件有 small 属性,应用小尺寸 */:host([small]) .button {padding: 8px 16px;font-size: 14px;border-radius: 20px;}</style><!-- 注意:这里使用了 <slot> --><button class="button"><slot></slot> <!-- 这里会显示用户写在 <fancy-button> 标签之间的内容 --></button>`;}// 生命周期钩子:当元素被添加到页面时调用connectedCallback() {console.log('FancyButton 已加载到页面!');// 这里可以添加事件监听等逻辑// 例如:this.shadowRoot.querySelector('button').addEventListener('click', ...)}
}// 4. 注册自定义元素
// 第一个参数是标签名,必须包含连字符
// 第二个参数是类
customElements.define('fancy-button', FancyButton);

代码详解:

  • class FancyButton extends HTMLElement:创建一个类,继承原生 HTML 元素的能力。
  • super():必须调用父类构造函数。
  • this.attachShadow({ mode: 'open' }):创建影子 DOM,实现样式和 DOM 的封装。
  • this.shadowRoot.innerHTML = ...:在影子 DOM 中写入 HTML 结构和 CSS 样式。样式不会“泄漏”出去,也不会被外部样式影响。
  • <slot></slot>:这是一个“插槽”,它会自动显示你在 <fancy-button>内容</fancy-button> 标签之间写的内容。
  • :host([primary]):host 选择器指向自定义元素本身。[primary] 表示当元素有 primary 属性时,应用后面的样式。这是通过属性控制样式的技巧。
  • customElements.define():将类注册为一个可以在 HTML 中使用的标签。

第二步:创建 HTML 页面 (index.html)

现在我们来使用刚刚创建的组件。

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0"/><title>Web Component 入门案例</title><!-- 5. 引入组件的 JavaScript 文件 --><script type="module" src="./fancy-button.js"></script><style>body {font-family: Arial, sans-serif;padding: 40px;background-color: #f0f2f5;}h1 {color: #333;}/* 测试外部样式是否会干扰组件 */button {background: red !important; /* 不会生效!因为组件内部是隔离的 */}</style>
</head>
<body><h1>Web Component 入门案例</h1><p>下面是一些使用 <code>&lt;fancy-button&gt;</code> 的例子:</p><!-- 6. 直接使用自定义标签 --><fancy-button>普通按钮</fancy-button><br /><br /><!-- 使用 primary 属性 --><fancy-button primary>主色调按钮</fancy-button><br /><br /><!-- 使用 small 属性 --><fancy-button small>小按钮</fancy-button><br /><br /><!-- 组合使用属性 --><fancy-button primary small>小的主色调按钮</fancy-button><br /><br /><!-- 插槽可以插入任何内容,不仅仅是文字 --><fancy-button><strong>加粗的</strong> 按钮内容</fancy-button><br /><br /><!-- 对比:原生按钮 --><button>原生按钮 (样式被隔离,不受影响)</button>
</body>
</html>

代码详解:

  • <script type="module" src="./fancy-button.js"></script>:使用 type="module" 加载 JS 文件。这是现代浏览器加载 ES6 模块的方式,确保脚本在 DOM 解析完成后执行,避免注册顺序问题。
  • <fancy-button>内容</fancy-button>:直接像使用普通 HTML 标签一样使用你的自定义组件!
  • primary 和 small:通过添加属性来改变按钮的外观,这得益于 :host() 选择器。
  • <strong>加粗的</strong>:展示了 <slot> 的强大,它可以分发任何 HTML 内容。

Web Component 的魅力在于“原生”和“可复用”。一旦你创建了一个组件,它就可以在任何现代浏览器的任何项目中使用,无论项目是用 React、Vue 还是纯 HTML 构建的!

http://www.dtcms.com/a/524663.html

相关文章:

  • 有没有做产品团购的网站wordpress文章页禁止右键
  • Nginx 反向代理解析:从原理到生产级配置实战
  • [理论题] 2025 年 “技耀泉城” 海右技能人才大赛网络安全知识竞赛题目(四)
  • 文化馆网站数字化建设介绍重庆seo网站建设
  • 【Betaflight源码学习】之初始化函数(init.c)
  • STM32H750寄存器操作(硬件I2C)
  • 算法18.0
  • RHCA - DO374 | Day02:管理内容集和执行环境
  • 网站建设明细价格表包头seo营销公司
  • JAVA 锁机制【待完善】
  • 不平均的分治——根号分治
  • USP-(DeepSpeed-Ulysses-Attention and Ring-Attention)
  • Ubuntu部署集群环境(3台)
  • VoCo-LLaMA: Towards Vision Compression with Large Language Models 译读笔记
  • 国网北京电力建设研究院网站惠州网站建设电话
  • 鹤壁市住房和城乡建设局网站上线了建站教程
  • centos8.5运行ai00-server报错`GLIBC_2.39‘ not found,解决方法
  • 冷换仓的隐性代价:从安全策略到地址信誉体系的重新思考
  • 如何用Vue CLI 创建 Vue 项目
  • 网站开发专业前景完整php网站开发
  • 企业建网站110平米三室一厅简装图片
  • CAS汽车固件签名:从“完成签名”到“安全治理”的演进之路
  • 免费手机网站模板sem竞价推广怎么做
  • 企业数字化转型的关键一步:打通研发全流程
  • Unity 资源导出的问题,依赖关系过多。
  • 网站开发公司内部数据字典深圳网站建设设计公司
  • 网站建设维护公司地址如何做好网站内容
  • STM32_bug总结-运行函数在SystemInit之后就卡死
  • 基于高光谱成像技术的烟叶含水率检测研究进展
  • 聊城网站推广网站可以换域名吗