谈谈WebComponents | 前端开发

eeaa637bb0b3ae55dbd787ebd793ccdc.png

一、 源起

让我们以一个例子开始。

假设我们要做一个环形进度条,它可以:

1、根据进度数值的不同,计算出百分比,以渲染对应的角度值。

2、根据设置的进度不同,我们用不同的颜色加以区分。

3、在环的中间我们以动画递增的方式显示进度。

最终的效果,大致如下图:

c54e9bc45ef1e17812de3890a4cb4860.gif

除了直接用组件库,聪明的你肯定已经想到了多种解决办法。如使用现代前端框架React/Vue/Angular/SvelteJS/SolidJS等等,你可能会找到或编写对应的组件,通过相应数据状态的变更,完成相对复杂的交互;又如在小快灵的项目下,用jQuery的Widget开发也是一个不错的选择;再或者,你可以点开你的HTML+JavaScript+CSS技能树,纯手工打造一个。这都是可以完成的任务。

这里[1]就是一个针对这个需求的实现。

当然,在完成之后,你可能会考虑对组件做一些提炼,下次再遇到同样的需求,你就可以气定神闲地“开箱即用”。通常,我们希望撰写的代码能够实现在UI以及行为层面的复用。所以,以组件为单位进行代码复用的需求也就呼之欲出。

实际上,除了Web前端外,各种相关的界面技术,比如安卓和苹果App、Windows软件、Qt以及Flutter等等,都发展出了类似的组件化技术。可以说,组件化是界面技术发展到一定复杂程度的必然产物。

为了放心地复用组件,除了代码层面的复用方案,也要包含一定的隔离特性:我们希望元素的样式、行为能够不被其他的代码干扰,也不会干扰到页面其他的元素,即存在一定的隔离性。考虑下面的代码:

5843f8983036b784ded9166b827430aa.png

考虑上述第10行到20行,以及21行到29行分别来自不同的组件。这里第二个组件的加入就会影响第一个组件的行为。第一个组件也在完全不知情的情况下被莫名其妙地变更了行为、样式。

当然,这样的代码是精心简化过的,实际上的工程代码不会这样撰写。但复杂的代码会造成问题被隐藏的更深,更加难以发现。我们当然可以直接诘问开发者的虑事不周,不过更理智的做法是提炼一些工程化的方法来避免这种问题的发生。比如Vue框架中的scoped样式。归根到底的问题在于,早期的HTML、CSS缺乏封装所必须的特性。

为了弥补这一缺陷,技术人员做了各种探索。

“上古时期”,微软在IE系列曾实现过一种名曰“HTC”的技术,我们甚至还能从互联网上找到这个技术的蛛丝马迹[2]。它实现了组件模版、样式、行为的复用和隔离方案。然而这项技术是IE Only的技术,后面随着大厂博弈以及各种标准化方案的诞生,这个方案在IE10以后便逐渐退出历史舞台。

Firefox在2007年左右也曾经支持过一个封装方案,叫XML Binding Language[3]。不过这项技术也由于主要在Firefox支持,也已经逐步淡出。

前端的框架如Angular、React、Vue等等,都提供了组件的复用和行为隔离的方案。不过由于浏览器层面对于前端框架的语法不支持,一般会需要在部署在生产环境前作一定的前端编译,这也是目前前端开发的标准做法。

针对这些需求,W3C标准化项目组,在2011年前后提出了WebComponents标准,现在由WhatWG进行维护。Web Components最终试图HTML、CSS和DOM API层面配合浏览器试图解决这些问题,是一种新的浏览器特性,同时也是一个复合的标准,提供了Web开发中组件的实现模型。

Web Components也是这篇文章的主角。

二、 标准细节

目前认为,Web Components是一组标准的集合。针对集合涵盖的内容,各个版本有不同的定义。由于篇幅所限,本文我们主要针对三个核心展开讲解,分别是Custom Elements、HTML template以及Shadow Dom。最新版本的浏览器还支持ES Modules,恰当的使用ES Modules,有利于模块的复用,在这种情况下,我们也可以把这个标准囊括进标准集合中。

2-1 Custom Elements

Custom Elements的出现,主要是为了解决HTML标签的有限性。它允许开发者自定义标签,并为其添加默认的行为、样式。

那么,如何来定义呢?下面是标准语法:

165f20b84c6d8954546880c56fd8ef73.png

这里是否传递第三个参数,决定了定义元素的类型。

如果增加了第三参数,则直接继承该HTML元素的行为,称为Customized built-in elements(自定义内置元素)。比如继承了HTMLImageElement,则继承了img标签元素的所有默认行为和样式。

如果缺省,该元素直接继承HTMLElement,称为Autonomous custom elements(自主自定义元素)。比如我们定义了一个user-card的标签,之后,我们就能直接使用<user-card></user-card>来引入这种标签的默认样式和行为。

我们首先来看一个自定义内置元素的例子:

28d79fb90c733b4aa16c9814fe3e3514.png

上述代码扩展了html默认的button元素,定义了一个hello-button组件。

这段代码有两个关键点:

1.CustomElements.define 需要指定第三个扩展参数。

2.使用时,使用is属性,来指定隶属的自定义元素名。

默认的,每个hello-button都会有button的所有属性,但增加了一点:点击会默认弹出“Hello!”。组件的示例依然可以定义事件,并且不会覆盖定义类上定义的事件。

需要说明的是,目前(2024年6月)safari浏览器最新版依然没有对自定义内置元素进行支持,Chrome、Edge、Firefox、Opera等都提供了支持。

接下来,我们再来看一下自主自定义元素。这个特性目前浏览器支持较好,主流的浏览器都已提供支持。

a378b4e78e8ebdb840efc7e39ca23410.png

之后我们在html里面加入:

7226a3a032a7d55461bf7cab745fae07.png

就可以显示了。这似乎有点像我们熟悉的React/Vue组件的用法了。

CustomElements支持生命周期的回调。

生命周期

说明

connectedCallback()

每当元素添加到文档中时调用。

disconnectedCallback()

每当元素从文档中移除时调用。

adoptedCallback()

每当元素被移动到新文档中时调用。

attributeChangedCallback()

在属性更改、添加、移除或替换时调用。

需配合静态的observedAttributes属性。

这里是一个在线的例子[4],读者可以尝试一下。

2-2 HTML template

上一节我们写的user-card组件,里面都是用dom方法动态创建的。这样不但麻烦,而且运行效率也偏低。为此,WebComponents标准提供了HTML templates的方式。

622295ec6cc527ab42042d72def8c3c5.png

<template>标签也可以多次复用。

尽管到这一步已经挺好了,但是元素仍旧不是很灵活。我们只能在里面显示一点文本,这里,可以使用 <slot> 插槽元素通过声明式的语法在每个元素实例中显示不同的文本。插槽由其 name 属性标识,并且允许开发者在模板中定义占位符,当在标记中使用该元素时,该占位符可以填充所需的任何 HTML 标记片段。

我们在上述代码的第18行增加下列代码:

b1c5aa4c6f70347b2b7551761fe6da7e.png

同时我们在调用时使用:

28e846924bab56918382aaa2ab4fc2f9.png

则完成了文字动态传入。

9667282ac6e22602ecf7ea136a3c0244.png

再进一步,我们索性支持外界传值支持一下。

76e93914a48f78795ae40f71697ea322.png

这样在调用时候,我们就可以用外界传递的图片了:

a6e4dfa9440132ec328c3a656a94d303.png

我们还可以在template标签里用<style>标签增加必要的样式。至此,我们基本上完成了这个自定义组件。这里是完整的代码[5]

之所以写在组件里,也就间接实现了封装。这样定义出的样式,理论上不会影响其他的元素。

这里:

4fb5843deb3802bd76c4e58d2250f276.png

伪类,选择包含使用这段 CSS 的 Shadow DOM 的影子宿主。具体到这个例子,我们的指的是这里:

8b2555d6ee8a0d28332b6dca1bd15987.png

为了最终实现封装,我们再来引出第三项技术:Shadow Dom。

2-3 Shadow Dom

自定义元素从定义上来说是一种可重用功能:它可以被放置在任何网页中,并且期望它能够正常工作。因此,很重要的一点是,运行在页面中的代码不应该能够通过修改自定义元素的内部实现而意外地破坏它。Shadow DOM允许你将一个 DOM 树附加到一个元素上,并且使该树的内部对于在页面中运行的 JavaScript 和 CSS 是隐藏的。

为了搞清Shadow DOM的机制,我们需要先厘清几个概念:

1.Shadow DOM: 是一种依附于文档原有节点的子 DOM,具有封装性。

2.Light DOM: 指原生的DOM节点,可以通过常规的API访问。Light DOM和Shadom DOM常常一起出现。这也是很有意思的一个比喻。一明一暗,灯下有影子。

3.Shadow Trees:Shadow DOM的树形结构。一般地,在Shadow Trees的节点不能直接被外部JavaScript的API和选择器访问到,但是浏览器会对这些节点做渲染。

4.Shadow Host:Shadow DOM所依附的DOM节点。

5.Shadow Root:Shadow Trees的根节点。外部JavaScript如果希望对Shadow Dom进行访问,通常会借助Shadow Root。

6.Shadow Boundary:Shadow Tree的边界,是JavaScript访问、CSS选择器访问的分界点。

7.content:指原本存在于Light DOM 结构中,被标签添加到影子 DOM 中的节点。自Chrome 53以后,content标签被弃用,转而使用template和slot标签。

8.distributed nodes:指原本位于Light DOM,但被content或template+slot添加到Shadow DOM 中的节点。

9.template:一致标签。类似我们经常用的<script type='tpl'>,它不会被解析为dom树的一部分,template的内容可以被塞入到Shadow DOM中并且反复利用,在template中可以设置style,但只对这个template中的元素有效。

10.slot:与template合用的标签,用于在template中预留显示坑位。

下面这幅图,显示了这些概念之间的关系:

7e65ae86c1250124bbce05b7f03a1a94.png

了解了Shadow DOM的概念,我们就可以利用Shadow Dom做一些事情了。de649d72083cf6206d8c16f670f1554f.png

这里注意下{mode: 'open'},此后通过div.shadowRoot即可拿到sr的实例。sr可以使用一般的JavaScript API来做相关的操作。

如果这里采用{mode: 'closed'},则此时div.shadowRoot为null。外部不可能再拿到sr的实例。此时外部很难操作到sr下的Shadow DOM,仅可以依靠Shadow内部的元素来进行操作。

下面是操作Shadow DOM样式的几种方法:

1.在Shadow DOM内部来操作Shadow Host的样式。

:host 允许你选择并样式化 Shadow Tree所寄宿的元素

576ffe54f0477f0659a04fd5c7d7b1a7.png

2.跨越Shadow Boundary的样式::part()

对于::part,在允许样式定义的Shadow DOM,给属性part赋值,样式选择器可以使用::part(属性值)即可实现指定样式,这与之前不同的是,Shadow Dom元素知道外界可能会对其某些元素进行变化,是可以控制变化范围的。需要注意的是,在::part()选择器后,子代选择器无效。如你不能使用::part(foo) span。

24c296b0963a514bf4e3beef4205bb02.png 

::part()选择器自Chrome73开始支持。之前的版本,可以考虑^和^^选择器,^和^^选择Shadow DOM在最新版本已经无效。

三、 与现代前端框架的关系

从之前的内容我们可以看到,Web Components标准中有若干内容与现代前端框架有异曲同工的感觉。同时,现代前端框架在设计时或多或少会参考Web Components的标准。VueJS的创始人尤雨溪曾明确表示过Vue的模版设计部分遵照了Web Components的标准。这也为以后浏览器能力逐渐增强,前端框架减负,提供了可能。

同时,现在大多数的前端框架都提供了和Web Components组件共存的机制。以Vue为例,官方更是提供了将Vue组件编译为Web Components自主自定元素方案和自主自定元素转化为Vue组件的工具链。

直接用Web Components标准撰写的组件,原则上不需要经过预编译环节,可以直接运行于浏览器,理论上会比其他前端框架性能有一定的优势。但是,由于该标准提供的API较原始,需要做进一步封装才能更好的使用。同时,标准缺乏对现今流行的MVVM的支持,使得前端框架在数据驱动开发模式上仍有用武之地。

Shadow Dom等标准的进展,客观上也使得前端微服务模式慢慢成型,使得qiankun等前端解决方案有了落地的基石。

四、 结语

本文我们介绍了Web Components标准的主要技术点。应该注意到的是,标准还在不断演化过程中,各家浏览器的支持也在不断完善。我们期待,不久的将来,藉由浏览器原生支持的组件化方案,能够大放光彩。

文内链接

[1].https://github.com/taisuke-j/progress-ring-component

[2].https://docs.microsoft.com/en-us/previous-versions/aa918246(v=msdn.10)

[3].https://www.slideshare.net/slideshow/xml-binding-language-20/155196#5

[4].https://jsbin.com/qorohov/8/edit?html,js,console,output

[5]. https://jsbin.com/qorohov/23/edit?html,output

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于RabbitMQ的异步消息传递:发送与消费

引言 RabbitMQ是一个流行的开源消息代理&#xff0c;用于在分布式系统中实现异步消息传递。它基于Erlang语言编写&#xff0c;具有高可用性和可伸缩性。在本文中&#xff0c;我们将探讨如何在Python中使用RabbitMQ进行消息发送和消费。 安装RabbitMQ 在 Ubuntu 上安装 Rabbi…

wps的domain转为shp矢量

wps的namelist制作、python出图和转矢量 简介 wps&#xff08;WRF Preprocessing System&#xff09;是中尺度数值天气预报系统WRF(Weather Research and Forecasting)的预处理系统。 wps的安装地址在GitHub上&#xff1a;https://github.com/wrf-model/WPS 下载完成后&…

注册中心不知选哪个?Zookeeper、Eureka、Nacos、Consul和Etcd 5种全方位剖析对比

本文给大家讲解 5 种常用的注册中心&#xff0c;对比其流程和原理&#xff0c;无论是面试还是技术选型&#xff0c;都非常有帮助。 对于注册中心&#xff0c;在写这篇文章前&#xff0c;我其实只对 ETCD 有比较深入的了解&#xff0c;但是对于 Zookeeper 和其他的注册中心了解甚…

pytorch统计学分布

1、pytorch统计学函数 import torcha torch.rand(2,2) print(a) print(torch.sum(a, dim0)) print(torch.mean(a, dim0)) print(torch.prod(a, dim0))print(torch.argmax(a, dim0)) print(torch.argmin(a, dim0)) print(torch.std(a)) print(torch.var(a)) print(torch.median…

AI进阶指南第四课,大模型优缺点研究?

在上一篇文章中&#xff0c;我主要探讨了LM模型与企业级模型的融合。 但是&#xff0c;在文末对于具体的大模型优缺点只是简单地说明了一下&#xff0c;并不细致。 因此&#xff0c;在这一节&#xff0c;我将更为细致地说明一下大模型的优缺点。 一&#xff0c;隐私安全 将L…

Python输入与输出基础

Python输入与输出基础 引言 Python是一种非常直观且功能强大的编程语言&#xff0c;它允许用户轻松地处理输入和输出操作。无论是从用户那里获取数据&#xff0c;还是将结果展示给用户&#xff0c;Python都提供了简单易用的函数和方法。 一、输入数据 在Python中&#xff0c…

UWB:DS-TWR( Double-sided two-way ranging)双边测距公式推导:为啥是乘法?

UWB DS-TWR&#xff08; Double-sided two-way ranging&#xff09;双边测距为啥是乘法&#xff1f;&#xff1f; 公式&#xff1a; 我们先看单边 Single-Sided Two-Way Ranging (SS-TWR) 单边很好理解。 symmetric double-sided TWR (SDS-TWR)对称的双边测距 再看双边 Trou…

LeetCode热题100——最长连续序列

给定一个未排序的整数数组 nums &#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 class Solution(object):def longestConsecutive(self, nums):""":t…

【MAVEN学习 | 第2篇】Maven工程创建及核心功能

文章目录 一. 基于IDEA的Maven工程创建1.1 Maven工程GAVP属性&#xff08;1&#xff09;GroupID 格式&#xff08;2&#xff09;ArtifactID 格式&#xff08;3&#xff09;Version版本号格式&#xff08;4&#xff09;Packaging定义规则 1.2 IDEA构建Maven JavaSE工程1.3 IDEA构…

kettle使用手册 安装9.0版本 建议设置为英语

0.新建转换的常用组件 0. Generate rows 定义一个字符串 name value就是字符串的值 0.1 String operations 字段转大写 去空格 1. Json input 来源于一个json文件 1.json 或mq接收到的data内容是json字符串 2. Json output 定义Jsonbloc值为 data, 左侧Fieldname是数据库…

VS2022(Visual Studio 2022)最新安装教程

1、下载 1、下载地址 - 官网地址&#xff1a;下载 Visual Studio Tools - 免费安装 Windows、Mac、Linux - 根据自己的电脑的 【操作系统】 灵活选择。 2、安装包 【此处为Windows系统安装包】 2、安装 1、打开软件 - 右击【以管理员身份打开】&#xff0c; 2、准备配置 …

昇思25天学习打卡营第03天|张量Tensor

何为张量&#xff1f; 张量&#xff08;Tensor&#xff09;是一个可用来表示在一些矢量、标量和其他张量之间的线性关系的多线性函数&#xff0c;这些线性关系的基本例子有内积、外积、线性映射以及笛卡儿积。其坐标在 &#x1d45b;维空间内&#xff0c;有  &#x1d45b;&a…

机器人控制系列教程之URDF文件语法介绍

前两期推文&#xff1a;机器人控制系列教程之动力学建模(1)、机器人控制系列教程之动力学建模(2)&#xff0c;我们主要从数学的角度介绍了机器人的动力学建模的方式&#xff0c;随着机器人技术的不断发展&#xff0c;机器人建模成为了机器人系统设计中的一项关键任务。URDF&…

聚合项目学习

首先建立一个总的工程目录&#xff0c;里边后期会有我们的父工程、基础工程(继承父工程)、业务工程&#xff08;依赖基础工程&#xff09;等模块 1、在总工程目录中&#xff08;open一个空的文件夹&#xff09;&#xff0c;首先建立一个父工程模块&#xff08;通过spring init…

地铁中的CAN通信--地铁高效安全运转原理

目前地铁采用了自动化的技术来实现控制,有ATC(列车自动控制)系统可以实现列车自动驾驶、自动跟踪、自动调度;SCADA(供电系统管理自动化)系统可以实现主变电所、牵引变电所、降压变电所设备系统的遥控、遥信、遥测;BAS(环境监控系统)和FAS(火灾报警系统)可以实现车站…

AS-V1000外部设备管理介绍(国标GB28181设备管理,可以管理的国标设备包括DVR/NVR、IPC、第三方国标28181平台)

目录 一、概述 1、视频监控平台介绍 2、外部设备定义&#xff08;接入的国标设备&#xff09; 二、外部设备管理 2.1 外部设备添加 &#xff08;1&#xff09;设备侧的配置 &#xff08;2&#xff09;平台侧的配置 2.2 外部设备信息的修改 三、外部通道管理 3.1 外部…

【技术追踪】SDSeg:医学图像的 Stable Diffusion 分割(MICCAI-2024)

这医学图像分割领域啊&#xff0c;终究还是被 Stable Diffusion 闯进去了~ SDSeg&#xff1a;第一个基于 Stable Diffusion 的 latent 扩散医学图像分割模型&#xff0c;在五个不同医学影像模态的基准数据集上超越了现有的最先进方法~ 论文&#xff1a;Stable Diffusion Segmen…

当设备树中出现多个同一节点的处理办法

当设备树中出现多个同一节点的处理办法 1.同一文件下有多个节点不同设备树调用同一节点需要#include "xxx.dtsi"3,vscode快速搜索文件 ctrlshiftp 去掉> 1.同一文件下有多个节点 覆盖规则&#xff1a; 同一层次的节点&#xff0c;后面的会覆盖前面的节点 memory…

如何在浏览器中查看网页的HTML源代码?

如何在浏览器中查看网页的HTML源代码&#xff1f; 浏览html网页&#xff0c;查看其源代码&#xff0c;可以帮助我们了解该版网页的信息以及架构&#xff0c;每个浏览器都是允许用户查看他们访问的任何网页的HTML源代码的。以下编程狮小师妹就介绍几个常见浏览器的查看网页 HTM…

STL中的迭代器模式:将算法与数据结构分离

目录 1.概述 2.容器类 2.1.序列容器 2.2.关联容器 2.3.容器适配器 2.4.数组 3.迭代器 4.重用标准迭代器 5.总结 1.概述 在之前&#xff0c;我们讲了迭代器设计模式&#xff0c;分析了它的结构、角色以及优缺点&#xff1a; 设计模式之迭代器模式-CSDN博客 在 STL 中&a…