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为了保证高可用,它支持…...
kubernetes/k8s驱逐机制总结篇
概述 k8s的驱逐机制是指在某些场景下,如node节点notReady、node节点压力较大等,将pod从某个node节点驱逐掉,让pod的上层控制器重新创建出新的pod来重新调度到其他node节点。这里也将kube-scheduler的抢占调度纳入到了驱逐的讨论范围内&#…...
Git gui教程---第七篇 Git gui的使用 返回上一次提交
1. 查看历史,打开gitk程序 2. 选中需要返回的版本,右键,然后点击Rest master branch to here 3.出现弹窗 每个选项我们都试一下,从Hard开始 返回的选项 HardMixedSoft Hard 会丢失所有的修改【此处的…...
Web 开发 Django 管理工具
上次为大家介绍了 Django 的模型,通过模型就可以操作数据库,从而就可以改变页面的展示内容,那问题来了,我们只能通过手动编辑模型文件来配置模型吗?当然不是,Django 为我们提供了强大的工具,可以…...
分类算法的评价指标
分类算法的评价指标 查准率、查全率、准确率、F1分数: 查准率(Precision): 模型预测为正例的样本中实际未正的比例。它关注的点在:预测为正例样本的准确性。 查全率(recall): 模型…...
智能工厂移动式作业轻薄加固三防平板数据采集终端
在这个高度自动化和数字化的环境中,数据采集变得尤为重要。为了满足这个需求,工业三防平板数据采集终端应运而生。工业三防平板数据采集终端采用了轻量级高强度镁合金材质,这使得它在保持轻薄的同时具有更强的坚固性。这种材质还具有耐磨防损…...
Python Flask token身份认证
首先安装依赖: pip install flask-jwt-extended 然后在主应用中(项目入口文件)加入以下代码: from flask import Flask from flask_jwt_extended import JWTManager #引入依赖 app Flask(__name__) app.config[JWT_SECRET_KEY…...
docker安装rabbitMQ
目录 1、拉取镜像 2、构造镜像 3、开启插件 4、开启安全组 5、访问 ui界面访问不到解决步骤 1、拉取镜像 docker pull rabbitmq 这里拉取的是最新镜像,若要指定版本可在后加上版本号即可;比如 docker pull rabbitmq:3.7.14 2、构造镜像 方式一:交…...
PDF如何转ppt?PDF转ppt的方法
PDF是一种广泛应用于文档传输和存储的格式,然而,在某些情况下,我们可能需要将PDF文件转换为PPT,以便更加灵活地编辑和展示内容。那么,PDF如何转ppt呢?在本文中,我们将介绍几种常用的方法和工具,…...
设计模式(8)外观模式
一、 1、使用背景:降低访问复杂系统的内部子系统时的复杂度,简化客户端之间的接口。 2、定义: 为子系统中的一组接口定义一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。完美地体现…...
Django(7)-项目实战-发布会管理
登录功能 模板页面 sign/templates/index.html <!DOCTYPE html> <html> <head><title>Login Page</title> </head> <body><h1>发布会管理</h1><form action"/login/" method"post"><la…...
网站备案 拨测/佛山网络排名优化
前言用来干什么:想干嘛干嘛为什么写:写来玩,学习node.js文件系统相关api;树结构这种东西还是挺不错的,会用会造才是真的会用了什么: fs.readdir(dir), fs.stat(dir).isFile(), path处理路径等思路ÿ…...
设计制作个人网站/国内网络销售平台有哪些
学习内容简单查询汇总分析复杂查询多表查询如何提高SQL查询效率简单查询创建学校数据库的表查找学生查询姓‘猴’的学生名单查询姓名中最后一个字是‘猴’的学生名单查询姓名中带‘猴’的学生名单select * from student where 姓名 like 猴%;select * from student where 姓名 …...
聊城手机网站建设方案/怎么注册域名网址
前面写了一个参数估计,现在也顺便把假设检验也总结一下吧,主要参考书还是那本《概率论与数理统计》(陈希孺)。 假设检验就是提出一个命题,根据样本判断对错。 问题提法 有一个已知分布的总体,其中个别参数未知。现在抽取的一组样本…...
wordpress 新安装 慢/金戈西地那非片
复制文件。 语法 FileCopy源,目标 FileCopy 语句语法包含以下命名参数: 部分说明source必需。 指定要复制的文件的名称的字符串表达式。 _源_可能包含目录或文件夹,和驱动器。目标必需。 指定的目标文件名称的字符串表达式。 _目标_可能包含目…...
最牛视频网站建设/长沙seo网站推广
你心中的阳光房是什么样的?又宽又厚的钢结构加上钢化玻璃组成的非常笨重的阳光房?冬天还好,可一到夏天升温极快,非常的闷热吗?情愿只做一个阳光顶用来遮阳挡雨也不做阳光房吗?现在这些都是过去式了…...
手机网站模板安装方法/百度官网下载安装免费
如果数据操作比较频繁就会产生大量的日志,在/usr/local/mysql /var/下面产生mysql-bin.0000* 类似的文件,而且一般都在几十MB到几个GB,更甚会吃掉整个硬盘空间,从来导致MySQL无法启动或报错,如vps论坛用户的反馈。 删除…...