Web Components详解-Custom Elements
目录
引言
演变过程
概述
使用方式
创建标签
定义标签
使用标签
获取标签
异步定义标签
升级标签
完整案例
结语
相关代码
参考文章
引言
随着项目体量的增大,组件化和模块化的优势也愈发明显了,构建可重复使用、独立、可互操作的组件变得尤为重要,在JS中我们可以通过class和函数对代码解耦,使某段代码可以复用。在TS中我们也可以通过模块对代码进行模块化开发,在HTML页面中同样有一种技术可以实现独立的、可复用的组件,这便是本篇文章讲到的Web Components
Web Components主要包括Custom Elements、Shadow DOM、HTML Templates和JavaScript这四部分,在后文及后续的文章中我会详细展开说说
演变过程
在熟悉web组件之前,我们可以了解一下早期的开发人员如何进行业务组件复用的?
聊聊我使用过的三种复用方式
一是使用JQ的load()进行ajax请求,将结果渲染到某个div或者组件中,以我原先公司的页面为例子,可以将图中画框的部分分别在多个html页面中实现,然后使用JQ进行请求加载到主页的标签中,达到组件化效果,此时如果要传递参数则可以通过url或者共用localstorage等形式共享状态
这样做确实可以将某个页面或者标签模块进行复用,但是缺点也很明显,一是页面加载是异步的,需要通过ajax请求html文件的形式完成,二是传参的方式仅限于query的方式,复杂的参数支持率较为薄弱
第二种是Iframe的方式,这种方式和jq的load类似,同样拥有独立上下文,可以在当前环境使用自己的CSS和JS,并且在加载时独立于主页面。当然这么做的缺点也是有的,和load函数一样,它的页面单独加载和渲染多出了性能开销,以及异步加载问题
最后一种是使用JS的代码进行动态HTML拼接,介于其强大的兼容性,动态HTML拼接在早期的前端开发中被广泛使用,并且能够在绝大多数浏览器上良好运行,比如:
function createCustomTag(tagName, text, attributes) {var tag = "<" + tagName;for (var attr in attributes) {if (attributes.hasOwnProperty(attr)) {tag += " " + attr + '="' + attributes[attr] + '"';}}tag += ">" + text + "</" + tagName + ">";return tag;
}
这么做的好处是兼容性高 ,灵活性强,原生JS即可支持;但是其缺点是使JS语法以及CSS样式的隔离变得困难,代码可读性和可维护性降低,最终也被摒弃。
那么回到新生代的web components,它能解决什么问题,又有什么注意点?感兴趣的话就接着往下看吧
概述
自定义元素(Custom Elements)在许多框架和UI组件中广泛使用,比如elementUI: <el-xxx></el-xxx>;vant:<van-xxx />等。开发者可以通过创建自定义的HTML元素,使其在页面中表现和使用类似于内置的HTML元素。开发者通过自定义元素创建自定义的HTML元素,使其在页面中表现和使用类似于内置的HTML元素,它是通过 JavaScript 创建一个自定义元素类,并通过继承HTMLElement类来定义其行为和样式。
使用方式
创建标签
首先是创建标签,通过继承HTMLElement类的方式来创建自定义标签的行为和样式。
在构造函数中可以进行自定义标签的初始化工作,例如设置默认属性、添加事件监听器等
class MyCustomElement extends HTMLElement {constructor() {super();this.textContent = "my-custom-element"// 自定义元素被创建时的初始化逻辑}
}
此外自定义标签类可以包含以下几种函数:
- 连接回调(connectedCallback):自定义元素被插入到DOM树中时调用
- 断开回调(disconnectedCallback):自定义元素从DOM树中删除时调用
- 移动回调(adoptedCallback):当自定义元素被移动到新文档时调用
- 属性变化回调(attributeChangedCallback):自定义元素的属性被添加、删除或修改时调用
- 静态属性(observedAttributes):指定attributeChangedCallback要监听哪些属性的数组
使用示例可以参考以下代码(具体效果及用法会在后文贴出)
class MyCustomElement extends HTMLElement {constructor() {super();// 自定义元素被创建时的初始化逻辑this.textContent = "my-custom-element"}connectedCallback() {// 元素被插入到DOM时调用console.log("元素被插入到DOM");}disconnectedCallback() {// 元素从DOM中移除时调用console.log("元素从DOM中移除");}adoptedCallback() {// 元素被移动到新文档时调用console.log("元素移动到新文档");}attributeChangedCallback(attrName, oldValue, newValue) {// 元素的属性被添加、删除或修改时调用console.log(`${attrName}属性的旧值:${oldValue},新值:${newValue}`);}// 或使用静态属性代替get方法static get observedAttributes() {// 指定要监听的元素的属性数组return ['name', 'date'];}
}
定义标签
在创建标签后,我们需要通过customElements.define来定义一个自定义标签
customElements.define("my-custom-element", MyCustomElement)
tips:需要注意的是创建的标签的中间必须带 '-' 短横线,与原生标签隔开,比如:custom-element,而像:customElement,-customElement,customElement-,这几种是无法作为自定义名称使用的。
define函数可以传入三个参数,第三个可选参数是ElementDefinitionOptions,这个参数在使用自定义元素继承时才会用到,当我们定义一个自定义元素时,可以选择让它继承自内置的 HTML 元素。参考下面的代码
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>CustomElements</title>
</head><body><button is="my-custom-button"></button><script>customElements.define("my-custom-button", class extends HTMLButtonElement {constructor() {super()this.textContent = "按钮";}connectedCallback() {this.addEventListener("click", () => console.log("点击了"))}}, { extends: "button" })// 继承自原生的按钮标签</script>
</body></html>
需要注意的是, 自定义标签类同样需要继承于对应的HTML标签类(比如:HTMLButtonElement),其次在标签中使用时需要增加is属性,相当于是对button进行拓展
使用标签
使用自定义元素的方式有两种,分别是通过document.createElement('my-custom-element')和直接在页面中使用<my-custom-element></my-custom-element>标签,这和原生语法一致,只需要把常用的div,a,span等标签换成自定义的标签即可
<body><my-custom-element>my-custom-element</my-custom-element><script>const ele = document.createElement("my-custom-element")ele.textContent = "my-custom-element"document.body.appendChild(ele)</script>
</body>
获取标签
通过customElements.get(elemName)可以获取标签为elemName的自定义标签类,如果在定义之前获取则显示未定义
console.log(customElements.get(elemName));// undefined
customElements.define(elemName, MyCustomElement)
console.log(customElements.get(elemName).name);// class MyCustomElement extends HTMLElement {...}
异步定义标签
使用customElements.whenDefined(elemName)函数可以在标签定义时触发回调函数
setTimeout(() => {customElements.define(elemName, MyCustomElement)
}, 1000)
customElements.whenDefined(elemName).then(console.log)// class MyCustomElement
console.log(customElements.get(elemName));// undefined
升级标签
升级标签的目的是将自定义标签(ele)和自定义标签的构造函数或类(MyCustomElement)进行绑定,使标签(ele)可以访问类(MyCustomElement)的属性及方法。通过customElements.upgrade(ele)函数可以对自定义标签进行升级操作
class MyCustomElement extends HTMLElement {bgColor = "red"constructor() {super();}
}
const elemName = "my-custom-element"
const ele = document.createElement(elemName)
customElements.define(elemName, MyCustomElement)
console.log(Reflect.ownKeys(ele), ele.bgColor);// [] undefined
customElements.upgrade(ele);// 升级ele,使其与自定义标签类绑定,也就是说可以访问MyCustomElement的属性及方法
console.log(Reflect.ownKeys(ele), ele.bgColor);// ['bgColor'] red
完整案例
最后我们结合上面的知识点,实现一个完整的自定义标签的示例
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>CustomElements</title>
</head><body><iframe src="./temp.html" width="100" height="100"></iframe><script>const elemName = "my-custom-element"const ele = document.createElement(elemName)const iframeEle = document.querySelector("iframe")class MyCustomElement extends HTMLElement {bgColor = "red"constructor() {super();// 自定义元素被创建时的初始化逻辑this.textContent = elemName}connectedCallback() {// 元素被插入到DOM时调用console.log("元素被插入到DOM");}disconnectedCallback() {// 元素从DOM中移除时调用console.log("元素从DOM中移除");}adoptedCallback() {// 元素被移动到新文档时调用console.log("元素移动到新文档");}attributeChangedCallback(attrName, oldValue, newValue) {// 元素的属性被添加、删除或修改时调用console.log(`${attrName}属性的旧值:${oldValue},新值:${newValue}`);}// 或使用静态属性代替get方法static get observedAttributes() {// 指定要监听的元素的属性数组return ['name', 'date'];}}customElements.whenDefined(elemName).then(() => {const tempBox = iframeEle.contentDocument // 获取iframe的domdocument.body.appendChild(ele)// 元素被插入到DOMele.setAttribute("name", elemName)// name属性的旧值:null,新值:my-custom-elementele.setAttribute("date", "date")// date属性的旧值:null,新值:dateele.setAttribute("name", "my-custom-element2")// name属性的旧值:my-custom-element,新值:my-custom-element2ele.remove()// 元素从DOM中移除// 元素移动到新文档,通过iframe进行举例tempBox.body.appendChild(tempBox.adoptNode(ele))// 元素被插入到DOM})// 异步检查自定义标签定义,标签定义时触发该函数console.log(customElements.get(elemName));// 获取自定义标签,此时未定义console.log(ele instanceof MyCustomElement); // falseiframeEle.onload = () => {setTimeout(() => {// 加个setTimeout明显一点customElements.define(elemName, MyCustomElement)console.log(Reflect.ownKeys(ele), ele.bgColor);// [] undefinedcustomElements.upgrade(ele);// 升级ele,使其与自定义标签类绑定,也就是说可以访问MyCustomElement的属性及方法console.log(Reflect.ownKeys(ele), ele.bgColor);// ['bgColor'] redconsole.log(ele instanceof MyCustomElement);// trueconsole.log(customElements.get(elemName).name);// MyCustomElement , 获取自定义标签,此时已定义}, 1000);}</script>
</body></html>
上述代码展示了一个简单的自定义元素的定义和使用过程,包括自定义元素的构造函数、移动、生命周期回调方法以及属性变化回调方法的使用,以及customElements中的几种方法
结语
Custom Elements允许我们创建自定义的HTML元素,使其在页面中表现和使用类似于内置的HTML元素。我们可以通过继承HTMLElement类来定义自定义元素的行为和样式,并在构造函数中进行初始化工作。通过类的机制,我们可以达到复用自定义元素的目的。后面的系列文章我会基于其强大的Api与Shadow DOM和HTML Templates实现组件化效果,敬请期待。
文章到这也就结束了,感谢你的阅读,如果觉得文章对你有帮助的话,还望三连支持一下作者,感谢!
相关代码
WebComponents/CustomElements.html · 阿宇的编程之旅/myCode - Gitee.com
参考文章
Web components
Web Component - Web API 接口参考 | MDN
相关文章:

Web Components详解-Custom Elements
目录 引言 演变过程 概述 使用方式 创建标签 定义标签 使用标签 获取标签 异步定义标签 升级标签 完整案例 结语 相关代码 参考文章 引言 随着项目体量的增大,组件化和模块化的优势也愈发明显了,构建可重复使用、独立、可互操作的组件变得…...
批量将excel文件按照分类生成多个excel文件
要批量将Excel文件按照分类生成多个Excel文件,文件名为分类名,可以使用Python中的pandas库来实现。下面是示例代码: import pandas as pd import os def split_excel_by_category(file_path, category_column, output_folder): # 读取Ex…...
PCL 点云组件聚类
文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 该算法与欧式聚类、DBSCAN聚类很是类似,聚类过程如下所述: 1. 首先,我们需要提供一个种子点集合,对种子点集合进行初始的聚类操作,聚类的评估器(即聚类条件),可以指定为法向评估,也可以是距离评估,以此我…...

学习Linux的注意事项(使用经验;目录作用;服务器注意事项)
本篇分享学习Linux过程中的一些经验 文章目录 1. Linux系统的使用经验2. Linux各目录的作用3. 服务器注意事项 1. Linux系统的使用经验 Linux严格区分大小写Linux中所有内容以文件形式保存,包括硬件,Linux是以管理文件的方式操作硬件 硬盘文件是/dev/s…...
Java字符串
文章目录 String类String的特性String对象的创建String常用方法 StringBuilder类StringBuffer类StringBuffer对象的创建StringBuffer类的常用方法 String、StringBuffer、StringBuilder区别 存放的位置 java.lang.*; 继承的父类 java.lang.Object 实现的接口 java.io.Serializa…...

windows下安装go环境 和vscode中go扩展
1. 首先安装GO Go下载地址:go.dev 选择相对应的版本,下载,运行安装程序,并打开命令提示符,运行 go env ,确认已经安装go 注意关注其中GOPATH和GOROOT,这两个地址可以在系统环境变量中进行设置…...

C++ 面试题(一)--C++基础,面向对象,内存管理
目录 1.part1 C基础 1 C特点 2 说说C语言和C的区别 3 说说 C中 struct 和 class 的区别 4 include头文件的顺序以及双引号""和尖括号<>的区别 5 说说C结构体和C结构体的区别 6 导入C函数的关键字是什么,C编译时和C有什么不同? 7…...

ARTS打卡第二周之链表环的检测、gdb中disassemble的使用、底层学习建议、学习分享
Algorithm 题目:链表中环的检测 自己的分析见博客《检测链表中是否存在环》 Review disassemble command是我读的一篇英语文章,这篇文章主要是介绍gdb反汇编命令的使用和参数。自己为了能够演示这篇文章里边的内容,特意自己使用汇编语言编…...

皕杰报表(BIOS Report)中设置序号的方法之二
在皕杰报表如何设置序号系列之一里,我们用ds.#0来实现了序号,用ds.#0得到的数据库中选取的记录的序号。有些情况下,记录序号在报表中不是按照顺序显示的,而是在报表中又通过排序或分组后的结果显示的,例如:…...
nacos总结黑马
SpringCloud实用篇02 0.学习目标 1.Nacos配置管理 Nacos除了可以做注册中心,同样可以做配置管理来使用。 1.1.统一配置管理 当微服务部署的实例越来越多,达到数十、数百时,逐个修改微服务配置就会让人抓狂,而且很容易出错。我…...
【简单bat】写一个定时关机的批处理程序
感谢我那可爱的GPT助手对代码的优化工作。 echo off REM Author: liyang TEL 155-4089-9552echo 欢迎使用自动关机计划设置脚本!by LiYang echo.:choose_action set /p "action_choice请选择要执行的操作(输入1设置定时关机任务,输入2删…...
Oracle 19c 启动和关闭实例保存PDB状态
简介: 十年以上 MySQL Oracle DBA从业者,MySQL 5.7 OCP, 微信号: jinjushuke 当前有一个PDB 打开模式为READ WRITE [oracleDGMOGGM19C ~]$ sql sys192.168.3.107:1521/pdb1 as sysdba SQLcl: Release 19.1 Production on Wed Aug 23 10:19:…...

计算机毕设 基于机器学习的餐厅销量预测 -大数据 python
文章目录 0 前言餐厅销量预测模型简介2.ARIMA模型介绍2.1自回归模型AR2.2移动平均模型MA2.3自回归移动平均模型ARMA 三、模型识别四、模型检验4.1半稳性检验(1)用途(1)什么是平稳序列?(2)检验平稳性 ◆白噪声检验(纯随机性检验)(1)用途(1)什么是纯随机序列?(2)检验纯随机性 五…...
layui 多选限制选择3个
可以使用 layui 的表单组件进行多选,然后通过 JavaScript 代码限制用户最多只能选择 3 个选项。具体的实现步骤如下: 在 layui 的表单组件中,使用多选框进行多选。 <div class"layui-form-item"><label class"layu…...
PyQt PySide6 QMessageBox使用教程
PySide6 是一个 Python 绑定到 Qt6 库的库,允许你在 Python 中创建 Qt6 应用程序。QMessageBox 是一个提供模式对话框,用于显示消息、询问问题或获取简单的输入的类。 以下是使用 PySide6 中的 QMessageBox 的基本方式: 导入必要的模块: f…...

Visual Studio软件安装包分享(附安装教程)
目录 一、软件简介 二、软件下载 一、软件简介 Visual Studio是微软公司开发的一款集成开发环境(IDE),广泛应用于Windows平台上的应用程序和Web应用程序的开发。以下是Visual Studio软件的主要特点和功能: 集成开发环境&#x…...
VB电脑销售系统设计与实现
前 言 本文论述了销售管理系统的开发过程,它包括了软件的开发环境,开发工具以及相关技术、系统分析、系统设计、实现等内容。 本系统能为企业繁重的工作带来极大的方便,提高了工作效率及工作准确性,能准确无误地查找货物的综合信息,为企业减少了许多工作细节。 为了使此…...
如何访问MySQL错误日志
通常情况下,MySQL中出现减速、崩溃或其他意外行为的根本原因可以通过分析错误日志来确定。在Ubuntu系统中,MySQL的默认位置是/var/log/mysql/error.log。在许多情况下,使用less程序最容易读取错误日志,这是一个命令行实用程序&…...

redis主从复制详解
目录 前言: 分布式系统 主从模式 主从同步连接过程 replication id作用 offset作用 psync命令(psync replicationId offset) 全量复制 全量复制过程 部分复制 部分复制过程 实时复制 前言: redis为了保证高可用,它支持…...

【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...

学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...

srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...