Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Web Components 初探

Web Components 初探

作者头像
QQ音乐前端团队
发布于 2018-10-27 04:54:04
发布于 2018-10-27 04:54:04
2.7K00
代码可运行
举报
运行总次数:0
代码可运行

原文

任何UI框架或库最期望目标之一是帮助我们建立通用的模式或约定。这些约定使UI代码易于共享并为其提供理论基础。很长一段时间,每个框架或库都有自己的实现或UI组件版本。仅在该组件的生态中,这些组件可以实现代码复用。如果给定的UI组件/插件需要在不同的技术依赖中使用,往往由于特定的生态系统限制而成为局限。 例如,如果我编写一个Angular库并想在我的Vue应用程序中使用我的Angular下拉列表,目前还无法直接做到。如果有一个基于任意JavaScript库或框架编写的通用标准组件可以在任何浏览器中用,那就很好了!这是Web Components的愿景。

Web Components是标准化的底层浏览器API的集合,方便我们创建共享的可重用UI组件。在这次介绍中,我们通过构建一个Web组件的例子来介绍其中的一些API。我们的组件是一个简单的计数器组件,当有单击操作时实现数字加减。

为了制作我们的计数器组件,将介绍三种不同的API,Custom Elements,Templates 和Shadow DOM API。首先介绍Custom Elements API。

Custom Elements

Custom Elements API允许我们自定义HTML节点并添加行为给这些节点。使用Custom Elements API,我们还可以扩展原生HTML节点。首先,让我们创建一个最小的自定义节点。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class XCounter extends HTMLElement {    constructor() {        super();    }    connectedCallback() {        this.innerHTML = `            <p>Hello From Web Component</p>        `;    }}customElements.define('x-counter', XCounter);   

上面我们用最少的代码量来创建我们的第一个自定义节点。我们继承HTMLElement类来创建自定义节点。在我们的自定义类中,可以定义模板和我们想要的任何行为。在特定生命周期的钩子函数connectedCallback()中,我们将模板赋值给节点的innerHTML属性。构造函数执行完且节点插入DOM之后才会调用connectedCallback()方法。

在我们定义Custom Element类之后,我们需要注册该节点。 customElements.define('x-counter',XCounter);注册节点时,我们传递两个参数。类引用和我们将在HTML中引用的节点的名称。在命名我们的节点时,名称中必须至少有一个破折号。Custom Elements 命名规定至少需要一个破折号,以防止命名与现有HTML节点发生冲突。

此时我们已经创建了Web Component 的基本结构,下面通过添加一个完整的模板来创建我们的计数器组件。

Template and Shadow DOM APIs

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const template = document.createElement('template');template.innerHTML = `        <style>            button, p {            display: inline-block;            }        </style>        <button aria-label="decrement">-</button>            <p>0</p>        <button aria-label="increment">+</button>    `;class XCounter extends HTMLElement {    constructor() {        super();        this.root = this.attachShadow({ mode: 'open' });        this.root.appendChild(template.content.cloneNode(true));    }}customElements.define('x-counter', XCounter);

Template和Shadow DOM API将允许我们创建高度封装的、高性能的组件。首先我们声明一个新的template节点。使用document.createElement('template');我们可以定义一个独立的原生HTML template。创建template 让我们不必立即将其插入DOM就可以构建HTML节点树。通过使用template,我们可以做到只创建一次template,然后在每次创建组件实例时重复使用它。

我们通过Shadow DOM API而不是之前的innerHTML示例添加我们新创建的template。通过Shadow DOM API将模板添加到我们的组件,我们在构造函数中添加如下代码。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
this.root = this.attachShadow({ mode: 'open' });this.root.appendChild(template.content.cloneNode(true));

在节点的构造函数中创建Shadow DOM,从而无需使用connectedCallback钩子函数来创建。 Shadow DOM为我们的组件创造一个高度封装的且隔离的DOM环境。 Shadow DOM会保护我们的HTML不被全局CSS或外部JavaScript污染。例如,在我们上面的模板中,我们有以下CSS:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
button, p {    display: inline-block;}

在我们的Shadow DOM template中定义样式时,我们的Web组件中的按钮和段落标记将使用内联样式进行设置。样式不会全局泄露,全局CSS样式也不会覆盖我们的template。现在我们已经设置并创建了template,我们需要在按钮上添加一些click事件处理。

Properties

为了与Web Components通信,我们主要通过组件上定义的公共属性来进行数据传递。对于我们的组件,我们将创建一个公共属性value 。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const template = document.createElement('template');template.innerHTML = `<style>    button, p {    display: inline-block;    }</style><button aria-label="decrement">-</button>    <p>0</p><button aria-label="increment">+</button>`;class XCounter extends HTMLElement {    set value(value) {        this._value = value;        this.valueElement.innerText = this._value;    }    get value() {        return this._value;    }    constructor() {        super();        this._value = 0;        this.root = this.attachShadow({ mode: 'open' });        this.root.appendChild(template.content.cloneNode(true));        this.valueElement = this.root.querySelector('p');        this.incrementButton = this.root.querySelectorAll('button')[1];        this.decrementButton = this.root.querySelectorAll('button')[0];        this.incrementButton            .addEventListener('click', (e) => this.value++);        this.decrementButton            .addEventListener('click', (e) => this.value--);    }}customElements.define('x-counter', XCounter);

在我们的组件上,我们为value属性创建了一个get和set方法。使用getter和setter,我们可以触发对template的更新。我们有一个私有的变量value来保留计数器值。在我们的setter中,我们使用下面的代码来更新数值:this.valueElement.innerText = this.value;。

使用我们的公共的 value getter方法,我们可以动态设置计数器的值。例如,我们获得对节点的引用,像其他HTML节点一样与它进行交互。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<x-counter></x-counter>
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import 'counter.js';const counter = document.querySelector('x-counter');counter.value = 10;

如您所见,我们可以查询自定义节点并像设置任何其他原生HTML节点一样设置自定义属性。使用我们的组件,我们可以通过输入属性将数据传递给它,但是如果我们希望组件在用户更改计数器值时通知我们怎么办?接下来,我们将介绍自定义事件。

Events

就像任何HTML节点一样,我们的自定义节点可以发出自定义事件供我们监听。在我们例子中,我们想知道用户何时更新了计数器组件的值。我们来看看组件值的更新。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const template = document.createElement('template');template.innerHTML = `<style>    button, p {    display: inline-block;    }</style><button aria-label="decrement">-</button>    <p>0</p><button aria-label="increment">+</button>`;    class XCounter extends HTMLElement {    set value(value) {        this._value = value;        this.valueElement.innerText = this._value;        // trigger our custom event 'valueChange'        this.dispatchEvent(new CustomEvent('valueChange', { detail: this._value }));    }    get value() {        return this._value;    }    constructor() {        super();        this._value = 0;        this.root = this.attachShadow({ mode: 'open' });        this.root.appendChild(template.content.cloneNode(true));        this.valueElement = this.root.querySelector('p');        this.incrementButton = this.root.querySelectorAll('button')[1];        this.decrementButton = this.root.querySelectorAll('button')[0];        this.incrementButton            .addEventListener('click', (e) => this.value++);        this.decrementButton            .addEventListener('click', (e) => this.value--);    }}customElements.define('x-counter', XCounter);

使用我们的组件,我们在value属性的setter方法中添加一个自定义事件。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
this.dispatchEvent(new CustomEvent('valueChange', { detail: this._value }));

我们可以发送自定义事件。自定义事件类有两个参数。第一个参数是事件的名称;第二个参数是我们想要传回的数据。通常会传递包含已更改数据detail属性的对象。当我们的自定义事件发出时,我们能够监听事件,同时获取事件值以及节点触发事件的详细信息。为了监听事件,我们可以像标准HTML节点一样创建事件监听器。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import 'counter.js';const counter = document.querySelector('x-counter');counter.value = 10;counter.addEventListener('valueChange', v => console.log(v));

在我们的代码中,我们可以监听自定义valueChange事件,在这里我们记录该值。

Attributes

有时,通过特性而不是属性将信息传递给组件也很方便。例如,我们可能想要传递一个初始值给我们的计数器。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<x-counter value="5"></x-counter>

为此,我们需要为组件添加一些额外的代码。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const template = document.createElement('template');template.innerHTML = `<style>    button, p {    display: inline-block;    }</style><button aria-label="decrement">-</button>    <p>0</p><button aria-label="increment">+</button>`;    class XCounter extends HTMLElement {    // Attributes we care about getting values from.    static get observedAttributes() {        return ['value'];    }    set value(value) {        this._value = value;        this.valueElement.innerText = this._value;        this.dispatchEvent(new CustomEvent('valueChange', { detail: this._value }));    }    get value() {        return this._value;    }    constructor() {        super();        this._value = 0;        this.root = this.attachShadow({ mode: 'open' });        this.root.appendChild(template.content.cloneNode(true));        this.valueElement = this.root.querySelector('p');        this.incrementButton = this.root.querySelectorAll('button')[1];        this.decrementButton = this.root.querySelectorAll('button')[0];        this.incrementButton            .addEventListener('click', (e) => this.value++);        this.decrementButton            .addEventListener('click', (e) => this.value--);    }    // Lifecycle hook called when a observed attribute changes    attributeChangedCallback(attrName, oldValue, newValue) {        if (attrName === 'value') {            this.value = parseInt(newValue, 10);        }    }}customElements.define('x-counter', XCounter);

在我们的组件上,我们必须添加两个部分。首先是我们希望在更改时收到通知的特性列表。这是Web Components所需的性能优化。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static get observedAttributes() {    return ['value'];}

在我们的组件上,我们还添加了一个生命周期中新的钩子函数。当HTML特性有更新时,就会调用attributeChangedCallback()

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
attributeChangedCallback(attrName, oldValue, newValue) {    if (attrName === 'value') {        this.value = parseInt(newValue, 10);    }}

对于Web通信最佳实践,最好使用自定义属性而不是自定义特性。属性更灵活,可以处理复杂的数据类型,如对象或数组。使用属性时,因为HTML的限制所有值都被当做String类型。自定义特性虽然很有用,但始终从属性开始,并根据需要添加特性。如果使用Web Component创作工具(如StencilJS),该工具会自动连接属性中的特性并使其保持同步。

总结

使用Web Components,我们可以创建可重用的Web UI组件库。Web components在Chrome,Safari中已经支持,很快Firefox便会支持。通过polyfill,我们还可以支持Edge(现在正在实现Web Component API)和IE11。大多数现代JavaScript框架也支持Web Components,您可以在custom-elements-everywhere.com上看到完全支持。

想要深入了解有关Web Components的更多信息?查看我的早期发行书Web Component Essentials!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-10-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 QQ音乐前端团队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Web Components不依赖前端框架
此文仅做了解,个人觉得web components发展起来还是需要一些时间的,并不着急掌握
javascript.shop
2019/09/04
8070
Web Components不依赖前端框架
Web Components 上手指南
现在的前端开发基本离不开 React、Vue 这两个框架的支撑,而这两个框架下面又衍生出了许多的自定义组件库:
童欧巴
2021/03/18
1K0
Web Components 上手指南
秒懂 Web Component
最近不是写了一篇关于京东微前端框架的文章嘛 《初探 MicroApp,一个极致简洁的微前端框架》,里面提到了一个叫 Web Components 的东西。虽然对它早有耳闻,但是一直也没怎么仔细看过,所以就深入了解了一下,今天给大家做个简单分享 :)
写代码的海怪
2022/03/29
7530
秒懂 Web Component
Web Components 系列(五)—— 详解 Slots
熟悉 Vue 的同学应该都知道”插槽(slot)“的概念,通过使用插槽可以让页面内容的组织更加灵活。
编程三昧
2022/02/12
1.4K0
Web Components 系列(五)—— 详解 Slots
Web Components系列(三) —— 创建 Custom Elements
根据前面的介绍,我们知道根据是否继承基本 HTML 元素,可以将自定义元素分为两类“
编程三昧
2022/02/09
5350
Web Components系列(三) —— 创建 Custom Elements
尤大 3 天前发在 GitHub 上的 vue-lit 是啥?
尤大北京时间 9月18日 下午的时候发了一个微博,人狠话不多。看到这个表情,大家都知道有大事要发生。果然,在写这篇文章的时候,上 GitHub 上看了一眼,刚好碰上发布:
lucifer210
2020/09/24
9710
尤大 3 天前发在 GitHub 上的 vue-lit 是啥?
Web Components 笔记
关键字:ES2015类的自定义元素写法、全局声明自定义元素、shadowroot,生命周期事件
申君健
2020/03/27
4450
写给小白的教程之WebComponent(1)
前两天有个同学问我,然后组内同学也即将分享相关实践内容,此处 在他之前发出(早一天发布的发布会),good!
马克付
2022/12/12
3240
写给小白的教程之WebComponent(1)
试试原生 Web Component: 比你想象的容易
<template>是一个HTML元素,它允许我们创建一个模板——web组件的HTML结构。模板不必是一大块代码。它可以很简单:
前端修罗场
2022/07/29
8060
试试原生 Web Component: 比你想象的容易
web components的一些知识点
上篇文章大致说了一下用web-components重新封装UI库的思路,并附了一个demo,代码写的垃圾,无所谓了,重要的是有思路才行。这次大致对web components这个东西,简单的做下介绍。
terrence386
2022/07/14
4930
web components 从0到1
Web Components 是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的web应用中使用它们。
copy_left
2021/12/08
3440
Web Components 并没有你想象中的那么复杂
我参加了一个技术交流大会,看到一些大佬演示 Web Components,感觉它非常漂亮,但是好像有点复杂过头了 —— 用近千行的 JavaScript 代码来表示个位数行数的 HTML 代码。大佬们总是不可避免地掩盖 Web Components 需要大量的 JavaScript 代码才能正常运行的这个问题,或者深入一些看起来无关紧要的细节。到了这个环节我的目光就开始变得呆滞,开始发呆。
童欧巴
2021/03/18
5810
Web Components 并没有你想象中的那么复杂
Web components
Web components是一组Web平台API和用于创建和使用可重复使用的自定义HTML元素的规范,旨在帮助我们封装和打包其UI元素和功能,从而更容易构建模块化、可维护和可重用的Web应用程序组件。
zayyo
2024/02/05
1500
初识web-components & todolist实现
从字面意思可以知道这是自定义元素的意思。区别于原生html元素,我们可以自己定义它的行为。按照是否从原生html元素继承,可分下面两类
ACK
2020/01/14
8960
Web Components 的使用,从入门到基础
Web Components[以下简称"WC"],使用自定义元素(custom elements)来代替div,故能使用div的得房就能使用它。因此,使用WC,只需要在HTML中引入js文件即可。它不并不像目前主流的组件框架,需要外部支撑。例如,如果你要使用React组件,那你大概率的情况下要使用ReactJS。
前端修罗场
2023/10/07
4250
Web Components 的使用,从入门到基础
如何基于 WebComponents 封装 UI 组件库
作为一名前端攻城狮,相信大家也都在关注着前端的一些新技术,近些年来前端组件化开发已为常态,我们经常把重用性高的模块抽离成一个个的组件,来达到复用的目的,这样减少了我们的维护成本,提高了开发的效率。但是都有一个缺点离不开框架本身,因为我们浏览器本身解析不了那些组件。那么有没有一种技术也可以达到这种效果呢?答案就是今天的主角 Web Components。
政采云前端团队
2022/08/30
1.5K0
如何基于 WebComponents 封装 UI 组件库
每天 React, Vue, 你知道如何原生实现 WebComponent吗?
原文地址:https://juejin.cn/post/7034796986889043999
coder_koala
2021/12/01
8060
深入解剖前端,你不知道的Web 组件标准
组件化使得复杂的前端结构变得清晰,各个部分独立起来,高内聚低耦合,使得维护成本大大降低。
一墨编程学习
2018/11/22
1.1K0
如何编写一个原生 Web Components 组件
在今天的前端编程中,利用语义化的 HTML 结合 CSS 来完一个组件并不是一件难事,这也意味着无论在 React、Vue 中都可以插入,不过它俩不是今天的主角,接下来我将用一个例子来介绍如何封装一个完整的原生 HTML 的 Web Components 组件,让我们开始吧!
茶无味的一天
2022/10/07
7930
如何编写一个原生 Web Components 组件
Web Components:自定义元素与Shadow DOM的实践
Web Components是现代Web开发中用于创建可重用和封装的自定义HTML元素的一组技术。它包括Custom Elements、Shadow DOM、HTML Templates和Slots。
天涯学馆
2024/05/17
2840
相关推荐
Web Components不依赖前端框架
更多 >
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验