Web 组件完整介绍

我们都有我们不会从事的项目。代码变得难以管理,范围不断扩大,快速修复应用在其他修复之上,结构在其意大利面条代码的重压下崩溃了。编码可能是一项混乱的业务。

项目受益于使用具有单一职责的简单、独立的模块。模块化代码被封装,因此无需担心实现。只要您知道在给定一组输入时模块将输出什么,您就不一定需要了解它是如何实现该目标的。

将模块化概念应用于单一编程语言很简单,但 Web 开发需要多种技术组合。浏览器解析HTML、CSS 和 JavaScript 以呈现页面的内容、样式和功能。

它们并不总是很容易混合,因为:

  • 相关代码可以在三个或更多文件之间拆分,并且
  • 全局样式和 JavaScript 对象可能会以意想不到的方式相互干扰。

这些问题是语言运行时、框架、数据库和服务器上使用的其他依赖项所遇到的问题之外的问题。

什么是 Web 组件?

Web 组件是一种创建可在任何页面上重用的封装的、单一职责的代码块的方法。

考虑 HTML<video>标签。给定一个URL,查看者可以使用诸如播放、暂停、后退、前进和调整音量等控件。

提供了样式和功能,但您可以使用各种属性和 JavaScript API 调用进行修改。任何数量的<video>元素都可以放在其他标签内,它们不会冲突。

如果您需要自己的自定义功能怎么办?例如,显示页面上单词数的元素?没有 HTML<wordcount>标签(还)。

ReactVue.js等框架允许开发人员创建 Web 组件,其中内容、样式和功能可以在单个 JavaScript 文件中定义。这些解决了许多复杂的编程问题,但请记住:

  • 您必须学习如何使用该框架并随着代码的发展更新您的代码。
  • 为一个框架编写的组件很少与另一个框架兼容。
  • 框架在流行中起起落落。您将变得依赖于开发团队和用户的突发奇想和优先事项。
  • 标准的 Web Components 可以添加浏览器功能,这是单靠 JavaScript 难以实现的(例如 Shadow DOM)。

幸运的是,库和框架中引入的流行概念通常会进入 Web 标准。这花了一些时间,但 Web 组件已经到来。

将模块化概念应用于单一编程语言很简单,但 Web 开发需要多种技术组合。👩‍💻 在本指南中了解更多信息⬇️点击推文

Web 组件简史

在许多特定于供应商的错误开始之后,标准 Web 组件的概念由Alex Russell 在 2011 年的 Fronteers Conference 上首次提出。Google 的Polymer 库(基于当前提案的 polyfill)在两年后问世,但直到 2016 年,Chrome 和 Safari 才出现早期的实现。

浏览器供应商花时间协商细节,但 Web Components 在 2018 年被添加到 Firefox 中,在 2020 年被添加到 Edge 中(当时微软切换到 Chromium 引擎)。

可以理解的是,很少有开发人员愿意或能够采用 Web 组件,但我们终于通过稳定的 API 达到了良好的浏览器支持水平。并非一切都是完美的,但它们越来越成为基于框架的组件的可行替代方案。

即使你还不愿意放弃你最喜欢的,Web Components 与每个框架都兼容,并且 API 将在未来几年内得到支持。

每个人都可以查看预构建 Web 组件的存储库:

…但是编写自己的代码更有趣!

本教程完整介绍了不使用 JavaScript 框架编写的 Web 组件。您将了解它们是什么以及如何将它们用于您的 Web 项目。您将需要一些HTML5、CSS 和 JavaScript 知识。

Web 组件入门

Web 组件是自定义 HTML 元素,例如<hello-world></hello-world>. 名称必须包含破折号,以免与 HTML 规范中正式支持的元素发生冲突。

您必须定义一个 ES2015 类来控制元素。它可以命名任何东西,但 HelloWorld 是常见的做法。它必须扩展HTMLElement 接口,它表示每个 HTML 元素的默认属性和方法。

注意: Firefox允许您扩展特定的 HTML 元素,例如 HTMLParagraphElement、HTMLImageElement 或 HTMLButtonElement。这在其他浏览器中不受支持,并且不允许您创建 Shadow DOM。

为了做任何有用的事情,该类需要一个名为connectedCallback()的方法,当元素添加到文档时会调用该方法:

class HelloWorld extends HTMLElement {

  // connect component
  connectedCallback() {
    this.textContent = 'Hello World!';
  }

}

在此示例中,元素的文本设置为“Hello World”。

该类必须向CustomElementRegistry注册以将其定义为特定元素的处理程序:

customElements.define( 'hello-world', HelloWorld );

现在,当您的 JavaScript 被加载(例如)时,浏览器会将<hello-world>元素与您的HelloWorld<script type="module" src="./helloworld.js"></script>类相关联。

您现在有一个自定义元素!

CodePen 演示

这个组件可以像任何其他元素一样在 CSS 中设置样式:

hello-world {
  font-weight: bold;
  color: red;
}

添加属性

这个组件没有好处,因为无论如何都会输出相同的文本。像任何其他元素一样,我们可以添加 HTML 属性:

<hello-world name="Craig"></hello-world>

这可能会覆盖文本,因此“Hello Craig!” 被展示。为此,您可以向HelloWorld类添加一个constructor()函数,该函数在创建每个对象时运行。它必须:

  1. 调用super()方法来初始化父 HTMLElement,并且
  2. 进行其他初始化。在这种情况下,我们将定义一个设置为默认值“World”的名称属性:
class HelloWorld extends HTMLElement {

  constructor() {
    super();
    this.name = 'World';
  }

  // more code...

您的组件只关心name属性。静态observedAttributes()属性应返回要观察的属性数组:

// component attributes
static get observedAttributes() {
  return ['name'];
}

当在 HTML 中定义属性或使用 JavaScript 更改属性时,将调用attributeChangedCallback()方法。它传递了属性名称、旧值和新值:

// attribute change
attributeChangedCallback(property, oldValue, newValue) {

  if (oldValue === newValue) return;
  this[ property ] = newValue;

}

在此示例中,只会更新name属性,但您可以根据需要添加其他属性。

最后,您需要在connectedCallback()方法中调整消息:

// connect component
connectedCallback() {

  this.textContent = `Hello ${ this.name }!`;

}

CodePen 演示

生命周期方法

浏览器在 Web 组件状态的整个生命周期中自动调用六个方法。此处提供了完整列表,尽管您已经在上面的示例中看到了前四个:

构造函数()

它在组件第一次初始化时被调用。它必须调用super()并且可以设置任何默认值或执行其他预渲染过程。

静态observedAttributes()

返回浏览器将观察到的属性数组。

属性更改回调(属性名称,旧值,新值)

每当观察到的属性发生变化时调用。在 HTML 中定义的那些会立即传递,但 JavaScript 可以修改它们:

document.querySelector('hello-world').setAttribute('name', 'Everyone');

发生这种情况时,该方法可能需要触发重新渲染。

连接回调()

当 Web 组件附加到文档对象模型时调用此函数。它应该运行任何所需的渲染。

断开回调()

当从文档对象模型中删除 Web 组件时调用它。如果您需要清理,例如删除存储的状态或中止Ajax 请求,这可能很有用。

通过回调()

当 Web 组件从一个文档移动到另一个文档时调用此函数。尽管我一直在努力考虑任何情况,但您可能会发现它的用途!

Web 组件如何与其他元素交互

Web 组件提供了一些您在 JavaScript 框架中找不到的独特功能。

影子 DOM

虽然我们在上面构建的 Web 组件可以正常工作,但它不能免受外部干扰,CSS 或 JavaScript 可以修改它。同样,您为组件定义的样式可能会泄漏并影响其他人。

Shadow DOM 通过将分离的 DOM 附加到 Web 组件来解决这个封装问题:

const shadow = this.attachShadow({ mode: 'closed' });

模式可以是:

  1. “open” ——外部页面中的 JavaScript 可以访问 Shadow DOM(使用Element.shadowRoot),或者
  2. “关闭” ——Shadow DOM 只能在 Web 组件中访问。

Shadow DOM 可以像任何其他 DOM 元素一样被操作:

connectedCallback() {

  const shadow = this.attachShadow({ mode: 'closed' });

  shadow.innerHTML = `
    <style>
      p {
        text-align: center;
        font-weight: normal;
        padding: 1em;
        margin: 0 0 2em 0;
        background-color: #eee;
        border: 1px solid #666;
      }
    </style>

    <p>Hello ${ this.name }!</p>`;

}

该组件现在在元素内呈现“Hello”文本<p>并为其设置样式。它不能被组件外部的 JavaScript 或 CSS 修改,尽管某些样式(例如字体和颜色)是从页面继承的,因为它们没有明确定义。

CodePen 演示

此 Web 组件范围内的样式不会影响页面上的其他段落甚至其他<hello-world>组件。

请注意,CSS选择器可以在 Web 组件:host中设置外部元素的样式:<hello-world>

:host {
  transform: rotate(180deg);
}

您还可以设置元素使用特定类时应用的样式,例如<hello-world class="rotate90">

:host(.rotate90) {
  transform: rotate(90deg);
}

HTML 模板

对于更复杂的 Web 组件,在脚本中定义 HTML 可能变得不切实际。模板允许您在页面中定义 Web 组件可以使用的 HTML 块。这有几个好处:

  1. 您可以调整 HTML 代码,而无需在 JavaScript 中重写字符串。
  2. 无需为每种类型创建单独的 JavaScript 类即可自定义组件。
  3. 在 HTML 中定义 HTML 更容易——并且可以在组件呈现之前在服务器或客户端上对其进行修改。

模板是在<template>标签中定义的,并且可以分配一个 ID,以便您可以在组件类中引用它。本例三段显示“Hello”消息:

<template id="hello-world">

  <style>
    p {
      text-align: center;
      font-weight: normal;
      padding: 0.5em;
      margin: 1px 0;
      background-color: #eee;
      border: 1px solid #666;
    }
  </style>

  <p class="hw-text"></p>
  <p class="hw-text"></p>
  <p class="hw-text"></p>

</template>

Web 组件类可以访问该模板,获取其内容,并克隆元素,以确保您在使用它的任何地方都创建一个唯一的 DOM 片段:

const template = document.getElementById('hello-world').content.cloneNode(true);

可以修改 DOM 并将其直接添加到 Shadow DOM:

connectedCallback() {

  const

    shadow = this.attachShadow({ mode: 'closed' }),
    template = document.getElementById('hello-world').content.cloneNode(true),
    hwMsg = `Hello ${ this.name }`;

  Array.from( template.querySelectorAll('.hw-text') )
    .forEach( n => n.textContent = hwMsg );

  shadow.append( template );

}

CodePen 演示

模板槽

插槽允许您自定义模板。假设您想使用<hello-world>Web 组件,但将消息放在 Shadow DOM 中的<h1>标题中。您可以编写以下代码:

<hello-world name="Craig">

  <h1 slot="msgtext">Hello Default!</h1>

</hello-world>

(注意slot属性。)

您可以选择添加其他元素,例如另一个段落:

<hello-world name="Craig">

  <h1 slot="msgtext">Hello Default!</h1>
  <p>This text will become part of the component.</p>

</hello-world>

现在可以在您的模板中实现插槽:

需要一个可以为您提供竞争优势的托管解决方案?Kinsta 为您提供令人难以置信的速度、最先进的安全性和自动缩放功能。查看我们的计划

<template id="hello-world">

  <slot name="msgtext" class="hw-text"></slot>

  <slot></slot>

</template>

一个元素槽属性设置为“msgtext”(<h1>)被插入到有一个<slot>名为“msgtext”的位置。<p>没有分配插槽名称,但在下一个可用的 unnamed 中使用<slot>。实际上,模板变为:

<template id="hello-world">

  <slot name="msgtext" class="hw-text">
    <h1 slot="msgtext">Hello Default!</h1>
  </slot>

  <slot>
    <p>This text will become part of the component.</p>
  </slot>

</template>

实际情况并非如此简单。<slot>Shadow DOM中的元素指向插入的元素。您只能通过定位 a<slot>然后使用.assignedNodes() 方法返回一个内部子级数组来访问它们。更新的connectedCallback()方法:

connectedCallback() {

  const
    shadow = this.attachShadow({ mode: 'closed' }),
    hwMsg = `Hello ${ this.name }`;

  // append shadow DOM
  shadow.append(
    document.getElementById('hello-world').content.cloneNode(true)
  );

  // find all slots with a hw-text class
  Array.from( shadow.querySelectorAll('slot.hw-text') )

    // update first assignedNode in slot
    .forEach( n => n.assignedNodes()[0].textContent = hwMsg );

}

CodePen 演示

此外,您不能直接设置插入元素的样式,尽管您可以针对 Web 组件中的特定插槽:

<template id="hello-world">

  <style>
    slot[name="msgtext"] { color: green; }
  </style>

  <slot name="msgtext" class="hw-text"></slot>
  <slot></slot>

</template>

模板槽有点不寻常,但一个好处是,如果 JavaScript 无法运行,您的内容将被显示。此代码显示了仅在 Web 组件类成功执行时才被替换的默认标题和段落:

<hello-world name="Craig">

  <h1 slot="msgtext">Hello Default!</h1>
  <p>This text will become part of the component.</p>

</hello-world>

因此,您可以实现某种形式的渐进增强——即使它只是“您需要 JavaScript”的消息!

声明性 Shadow DOM

上面的示例使用 JavaScript 构建了一个 Shadow DOM。这仍然是唯一的选择,但正在为Chrome开发一个实验性的声明性 Shadow DOM 。这允许服务器端渲染并避免任何布局变化或无样式内容的闪烁。

HTML 解析器检测到以下代码,它会创建与您在上一节中创建的相同的 Shadow DOM(您需要根据需要更新消息):

<hello-world name="Craig">

  <template shadowroot="closed">
    <slot name="msgtext" class="hw-text"></slot>
    <slot></slot>
  </template>

  <h1 slot="msgtext">Hello Default!</h1>
  <p>This text will become part of the component.</p>

</hello-world>

该功能在任何浏览器中都不可用,并且不能保证它会到达 Firefox 或 Safari。您可以找到有关声明性 Shadow DOM 的更多信息,并且 polyfill 很简单,但请注意实现可能会发生变化。

影子 DOM 事件

您的 Web 组件可以像在页面 DOM 中一样将事件附加到 Shadow DOM 中的任何元素,例如监听所有内部子级的点击事件:

shadow.addEventListener('click', e => {

  // do something

});

除非您stopPropagation,否则该事件将冒泡到页面 DOM 中,但该事件将被重新定位。因此,它似乎来自您的自定义元素,而不是其中的元素。

在其他框架中使用 Web 组件

您创建的任何 Web 组件都可以在所有JavaScript 框架中工作。他们都不知道也不关心 HTML 元素——你的<hello-world>组件将被视为与 a 相同,<div>并被放置在类将激活的 DOM 中。

custom-elements-everywhere.com提供了框架列表和 Web 组件说明。大多数都是完全兼容的,尽管 React.js 有一些挑战。可以<hello-world>在 JSX 中使用:

import React from 'react';
import ReactDOM from 'react-dom';
import from './hello-world.js';

function MyPage() {

  return (
    <>
      <hello-world name="Craig"></hello-world> 
    </>
  );

}

ReactDOM.render(<MyPage />, document.getElementById('root'));

…但:

  • React 只能将原始数据类型传递给 HTML 属性(而不是数组或对象)
  • React 无法侦听 Web 组件事件,因此您必须手动附加自己的处理程序。

Web 组件批评和问题

Web 组件已显着改进,但某些方面可能难以管理。

造型困难

样式化 Web 组件会带来一些挑战,尤其是在您想要覆盖作用域样式时。有很多解决方案:

  1. 避免使用 Shadow DOM。您可以将内容直接附加到您的自定义元素,尽管任何其他 JavaScript 都可能意外或恶意更改它。
  2. 使用:host类。正如我们在上面看到的,当一个类应用于自定义元素时,作用域 CSS可以应用特定的样式。
  3. 查看 CSS 自定义属性(变量)。自定义属性级联到 Web 组件中,因此,如果您的元素使用var(--my-color),您可以--my-color在外部容器(例如:root)中设置它,它将被使用。
  4. 利用阴影部分。新的::part() 选择器可以为具有 part 属性的内部组件设置样式,即组件<h1 part="heading">内部<hello-world>可以使用 selector 设置样式hello-world::part(heading)
  5. 传入一串样式。您可以将它们作为属性传递以在<style>块内应用。

没有一个是理想的,您需要仔细计划其他用户如何自定义您的 Web 组件。

忽略的输入

Shadow DOM 中的任何<input><textarea><select>字段都不会自动关联到包含的表单中。早期的 Web 组件采用者会将隐藏字段添加到页面 DOM 或使用FormData 接口来更新值。两者都不是特别实用和破坏 Web 组件封装。

新的 ElementInternals 接口允许 Web 组件表单挂钩,因此可以定义自定义值和有效性。它在 Chrome 中实现,但polyfill 可用于其他浏览器。

为了演示,您将创建一个基本<input-age name="your-age"></input-age>组件。该类必须将静态formAssociated值设置为 true,并且可以选择在关联外部表单时调用formAssociatedCallback()方法:

// <input-age> web component
class InputAge extends HTMLElement {

  static formAssociated = true;

  formAssociatedCallback(form) {
    console.log('form associated:', form.id);
  }

构造函数现在必须运行attachInternals()方法,该方法允许组件与表单和其他想要检查值或验证的JavaScript 代码进行通信:

 constructor() {

    super();
    this.internals = this.attachInternals();
    this.setValue('');

  }

  // set form value

  setValue(v) {

    this.value = v;

    this.internals.setFormValue(v);

  }

ElementInternal 的setFormValue()方法在此处为用空字符串初始化的父表单设置元素的值(也可以将其传递给具有多个名称/值对的 FormData 对象)。其他属性和方法包括:

  • form : 父表单
  • labels:标记组件的元素数组
  • 约束验证 API选项,例如 willValidate、checkValidity 和 validationMessage

connectedCallback()方法像以前一样创建一个 Shadow DOM,但还必须监视该字段的更改,因此可以运行setFormValue() :

 connectedCallback() {

    const shadow = this.attachShadow({ mode: 'closed' });

    shadow.innerHTML = `
      <style>input { width: 4em; }</style>
      <input type="number" placeholder="age" min="18" max="120" />`;

    // monitor input values
    shadow.querySelector('input').addEventListener('input', e => {
      this.setValue(e.target.value);
    });

  }

您现在可以使用这个 Web 组件创建一个 HTML 表单,它的行为方式与其他表单字段类似:

<form id="myform">

  <input type="text" name="your-name" placeholder="name" />

  <input-age name="your-age"></input-age>

  <button>submit</button>

</form>

它有效,但诚然感觉有点令人费解。

在CodePen 演示中查看它

有关更多信息,请参阅这篇关于功能更强大的表单控件的文章。

想要更好地了解 Web 组件及其工作原理?✅ 点击潜入⬇️点击推文

概括

在 JavaScript 框架的地位和功能不断增长的时候,Web 组件一直在努力获得共识和采用。如果您来自 React、Vue.jsAngular,Web 组件可能看起来复杂而笨重,尤其是当您缺少数据绑定和状态管理等功能时。

有一些问题需要解决,但 Web Components 的未来是光明的。它们与框架无关、轻量级、快速,并且可以实现仅在 JavaScript 中不可能实现的功能。

十年前,几乎没有人能够处理没有 jQuery 的网站,但浏览器供应商采用了优秀的部分并添加了本地替代方案(例如 querySelector)。JavaScript 框架也会发生同样的情况,而 Web 组件是第一步。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Axure是一种非常流行的原型设计工具,旨在帮助设计师和开发者创建交互式原型和无线框架设计。它提供了丰富的功能和组件库,其中之一是Axure web组件。 Axure web组件是一组特定的工具和元素,用于创建网页界面原型。它们基于web设计的最佳实践,并与实际的web开发代码相似。Axure web组件库包含各种UI元素,如按钮、文本输入框、下拉菜单、复选框等等,可以通过简单拖放的方式添加到原型中。 使用Axure web组件,设计师可以快速构建具有真实感的原型,以展示网站的功能和用户交互。这些组件提供了各种交互和动画效果选项,例如单击、拖放、滚动等等。设计师可以使用这些组件创建网页的基本布局和结构,并通过属性面板设置其样式和行为。 而且,Axure web组件还支持响应式设计,可以为不同的屏幕尺寸和设备创建不同版本的原型。这对于用户体验设计和网站开发非常重要,因为它可以帮助设计师在设计阶段就能够预览和测试不同屏幕上的网页响应效果。 总之,Axure web组件是Axure原型设计工具中的重要组成部分,用于创建具有交互性和真实感的网页原型。通过使用这些组件,设计师可以更好地展示他们的设计愿景,并与开发团队和利益相关者进行沟通和反馈。它是一个强大的工具,为用户体验设计和网站开发提供了极大的帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值