Web Components详解-Custom Elements

news2024/12/26 22:52:31

目录

引言

演变过程

概述

使用方式

创建标签

定义标签

使用标签

获取标签

异步定义标签

升级标签

完整案例

结语

相关代码

参考文章


引言

随着项目体量的增大,组件化和模块化的优势也愈发明显了,构建可重复使用、独立、可互操作的组件变得尤为重要,在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的dom
            document.body.appendChild(ele)// 元素被插入到DOM
            ele.setAttribute("name", elemName)// name属性的旧值:null,新值:my-custom-element
            ele.setAttribute("date", "date")// date属性的旧值:null,新值:date
            ele.setAttribute("name", "my-custom-element2")// name属性的旧值:my-custom-element,新值:my-custom-element2
            ele.remove()// 元素从DOM中移除
            // 元素移动到新文档,通过iframe进行举例
            tempBox.body.appendChild(tempBox.adoptNode(ele))// 元素被插入到DOM
        })// 异步检查自定义标签定义,标签定义时触发该函数
        console.log(customElements.get(elemName));// 获取自定义标签,此时未定义
        console.log(ele instanceof MyCustomElement); // false
        iframeEle.onload = () => {
            setTimeout(() => {// 加个setTimeout明显一点
                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
                console.log(ele instanceof MyCustomElement);// true
                console.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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/939751.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《自动驾驶与机器人中的SLAM技术》之GNSS相关基础知识总结

简介 本篇基于对《自动驾驶与机器人中的SLAM技术》中的GNSS定位相关基础知识进行总结用于备忘 知识点整理 GNSS(全球卫星导航系统)定位原理 GNSS 通过测量自身与地球周围各卫星的距离来确定自身的位置 , 而与卫星的距离主要是通过测量时间间隔来确定的 GNSS与GPS的关系 GPS(…

FilterListener

Filter&Listener 1 Filter1.1 Filter概述1.2 Filter快速入门1.2.1 开发步骤1.2.2 代码演示 1.3 Filter执行流程1.4 Filter拦截路径配置1.5 过滤器链1.5.1 概述1.5.2 代码演示 1.5.3 问题 1.6 案例1.6.1 需求1.6.2 分析1.6.3 代码实现1.6.3.1 创建Filter1.6.3.2 编写逻辑代码…

HHDESK一键改密功能

HHDESK新增实用功能——使用SSH连接&#xff0c;对服务器/端口进行密码修改。 1 测试 首页点击资源管理——客户端&#xff0c;选择需要修改的连接&#xff1b; 可以先对服务器及端口进行测试&#xff0c;看是否畅通&#xff1b; 右键——测试——ping&#xff1b; 以及右…

box_loss,obj_loss,cls_loss,Size的含义,以及他们数据增长与降低时,是好还是坏

box_loss,obj_loss,cls_loss,Size的含义&#xff0c;以及他们数据增长与降低时&#xff0c;是好还是坏

特斯拉启动墨西哥建厂计划,引发台厂竞逐 | 百能云芯

特斯拉&#xff08;Tesla&#xff09;在墨西哥新工厂计划备受瞩目&#xff0c;据外媒报道&#xff0c;墨西哥的超级工厂似乎正在迈出实质性的步伐。包括鸿海集团、广达&#xff08;Foxconn&#xff09;、和大在墨西哥和美墨边境都计划扩大电动车零配件生产基地。 市场对特斯拉在…

无法加载文件 D:\python-csv\venv\Scripts\Activate.ps1,因为在此系统上禁止运行脚本。

解决办法&#xff1a; 不可以直接删除该文件。该文件是 Python 虚拟环境的激活脚本&#xff0c;用于激活虚拟环境并设置环境变量。如果删除该文件&#xff0c;将无法激活虚拟环境&#xff0c;也无法使用虚拟环境中的 Python 解释器和库。 如果您无法加载该文件&#xff0c;可能…

【校招VIP】java语言考点之多线程NIO

考点介绍 多线程&NIO考点是校招面试中的常制点之一。 Java NIO是new IO的简称&#xff0c;是一种可以替代Java 10的一套新的IO机制。它提供了一套不同于Java标准1O的操作机制&#xff0c;严格来说&#xff0c;NIO与并发并无直接关系&#xff0c;但是使用NIO技术可以大大提高…

2023年夏季《移动软件开发》实验报告3

2023年夏季《移动软件开发》实验报告 本实验属于哪门课程&#xff1f;中国海洋大学23夏《移动软件开发》实验名称&#xff1f;实验3&#xff1a;口述校史博客地址&#xff1f;XXXXXXXGithub仓库地址&#xff1f;XXXXXXX &#xff08;备注&#xff1a;将实验报告发布在博客、代…

Android开发Demo:TextView同时显示图片和文本、同时显示多种颜色

一、预期效果 1、在一个Android TextView控件之中&#xff0c;同时显示文本和图片&#xff0c;如下图所示&#xff0c;文本之间掺夹着一张或多张图片。 2、在一个Android TextView控件之中&#xff0c;同时显示多种颜色&#xff0c;如下图所示&#xff0c;一条文本显示了两种不…

成都瀚网科技:抖店平台销量怎么更新?

抖店平台是一个以直播电商为核心的平台。对于店主来说&#xff0c;销售情况更新是衡量经营效益和产品受欢迎程度的重要指标之一。本文将探讨抖店平台销售额的更新方式和影响&#xff0c;帮助店主更好地了解销售额更新机制和影响因素。 1.如何更新抖店平台销量&#xff1f; 自动…

数据增强:提高机器学习性能的有效技巧

文章目录 数据增强的原理常用的数据增强技术图像数据增强文本数据增强音频数据增强 数据增强的代码示例拓展应用与挑战结论 &#x1f389;欢迎来到AIGC人工智能专栏~数据增强&#xff1a;提高机器学习性能的有效技巧 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&a…

树状表格子节点移动 - 在Vue.js中实现上下移动子节点的表格功能

目录 功能介绍 示例 代码 视图部分 逻辑部分 完整代码 功能介绍 本文介绍了如何在Vue.js框架下实现一个树状表格&#xff0c;其中支持选择子节点行的上下移动。通过这个功能&#xff0c;用户可以方便地改变子节点的顺序。代码示例和详细的实现步骤将展示如何使用Vue.js的相…

OJ题库:俩个有序序列(数组)合并

前言&#xff1a;在部分大厂笔试时经常会使用OJ题目&#xff0c;这里对《有序序列合并》进行思路分析和讲解&#xff0c;在这里主要使用俩种方法进行讲解&#xff0c;希望对各位读者有所帮助。 题目来自牛客网&#xff0c;欢迎各位积极挑战&#xff1a;有序序列合并_牛客题霸_牛…

汽车电子笔记之:基于AUTOSAR的多核监控机制

目录 1、概述 2、系统监控的目标 2.1、任务的状态机 2.2、任务服务函数 2.3、任务周期性事件 2.4、时间监控的指标 2.5、时间监控的原理 2.6、CPU负载率监控原理 2.6.1、设计思路 2.6.2、监控方法的评价 3、基于WDGM模块热舞时序监控方法 3.1、活跃监督 3.2、截至时…

Mongodb启动失败相关问题解决最全

一、mongod突然启动失败&#xff0c;存在mongod.lock文件 查找文件 find / -name mongod.lock -d删除该文件 rm -rf mongod.lock重新启动 /data/mongodb/bin/mongod --config /data/mongodb/bin/mongodb.conf --replSet cloud通过修改的方式启动 /data/mongodb/bin/mongod --r…

与信创国产化高度适配的低代码开发框架

信创产业是中国经济发展的基础&#xff0c;也是重要驱动力。坚持基础软件自主创新&#xff0c;是国产软件正版化应用和推广的源头。行业预测&#xff0c;2023年信创产业市场规模预计为18710.59亿元&#xff0c;预计到2025年将会接近3.5万亿元。 信创产业的主要目标是实现信息技…

屏蔽百度右侧热搜和首页新闻

先看效果 这样就没有垃圾新闻影响我们的注意力了 设置其实很简单&#xff0c;首先需要安装一下 Adblock Plus&#xff0c;安装的方式很多&#xff0c;这里我使用一个网站直接添加即可&#xff1a; Download Adblock Plus 3.18.1 CRX File for Chrome - Crx4Chrome 然后就能在…

Django(8)-静态资源引用CSS和图片

除了服务端生成的 HTML 以外&#xff0c;网络应用通常需要一些额外的文件——比如图片&#xff0c;脚本和样式表——来帮助渲染网络页面。在 Django 中&#xff0c;我们把这些文件统称为“静态文件”。 我们使用static文件来存放静态资源&#xff0c;django会在每个 INSTALLED…

【已解决】pycharm突然双击无法打开,重启电脑也不管用

1.问题&#xff1a; pycharm突然双击无法打开&#xff0c;重启电脑也不管用 2.解决 2.1 方法一&#xff08;修改Roaming&#xff09; 1.找到C盘对应路径下的pycharm版本 2. 用记事本打开文件类型为VMOPTIONS文件 3. 修改或删除最后一行的映射路径 4.保存退出 2.2 方法二…

无涯教程-Python机器学习 - Analysis of Silhouette Score函数

剪影得分的范围是[-1,1]。其分析如下- 1分数-接近1 剪影分数表示样本距离其邻近簇很远。 0分数-0 剪影分数表示样本在将两个相邻聚类分隔开的决策边界上或非常接近。 -1分数-1 剪影分数表示样本已分配给错误的聚类。 Silhouette得分的计算可以使用以下公式完成 $$剪影得…