如果你曾经使用过 React,你会很熟悉 Web 组件。它们是自定义的、可复制的 HTML 片段,可以在代码的其他地方引用。Web 组件是它们自己的 HTML 规范,因此您可能会惊讶于它们可以与纯 Javascript 和 HTML 一起独立使用。让我们来看看如何做到这一点。
想象一下,我们有一个状态栏项目,我们可以在多个地方重复使用它。它的结构可能如下所示:
<div class="status-bar"> <div class="size"> <div class="small">-</div> <div class="big">+</div> </div> <div class="status-bar-progress"> <div class="progress"> <div class="progress-percentage"></div> </div> </div> </div>
想象一下,必须将它粘贴到多个位置并对其进行维护,以使其看起来始终相同——如果有一天我们更改状态栏会怎样?我们需要返回所有出现状态栏的地方,并更新它们!Web 组件允许你在多个地方引用这段 HTML,并给它一个唯一的标签,即:
<status-bar></status-bar>
我们如何做到这一点?让我们看看如何制作自己的 Web 组件,以及它是多么容易。
步骤 1. Javascript#
是的,你猜对了,如果你想制作 web 组件,你需要使用 Javascript。让我们快速看一下如何创建一个非常简单的 DOM 元素,我们称之为“段落paragraph”。
class Paragraph extends HTMLElement { constructor() { super() this.innerHTML = '<p>Hello</p>' } } // define adds a custom element "alpha-paragraph" using the rules defined in the "Paragraph" class customElements.define('alpha-paragraph', Paragraph);
<alpha-paragraph></alpha-paragraph>
其输出将是一个段落,其中包含“Hello”一词。Web 组件依赖于类结构。我们constructor()
在这里使用来指示标签加载时会发生什么。
其他功能
除了构造函数,我们还可以将其他函数添加到一个类connectedCallback()
中,例如当 DOM 元素附加到页面attributeChangedCallback()
时触发,当元素的属性发生变化时触发,给我们一些灵活性:
class Paragraph extends HTMLElement { constructor() { super() this.innerHTML = '<p>Hello</p>' } connectedCallback() { // ... } attributeChangedCallback() { // ... } } customElements.define('alpha-paragraph', Paragraph);
步骤 2. 扩展功能#
现在我们有一种方法可以创建一个简单的元素,将回调事件附加到它,并将事件附加到属性更改,让我们考虑 CSS。在制作独立且可克隆的 Web 组件时,我们希望它在多个地方的物理外观相同。为此,它需要自己的自定义 CSS。我们如何将 CSS 添加到 Web 组件中?
为此,我们需要使用一种叫做shadow DOM的东西。这些本质上是仅附加到一个特定项目的项目。我们可以通过启用影子 DOM 并将我们的 CSS 附加到它来将其用于 Web 组件。
具有特定 CSS 的自定义元素
class Paragraph extends HTMLElement { constructor() { super() // Attach shadow DOM let shadow = this.attachShadow({mode: 'open'}); // Append our Paragraph shadow.innerHTML = '<p>Hello</p>' // Add in our CSS let style = document.createElement('style'); let elCss = style.textContent = ` p { color: red; font-size: 1.25rem; } `; // Append our CSS shadow.appendChild(style); } } customElements.define('alpha-paragraph', Paragraph);
在上面的示例中,任何alpha-paragraph
元素都会有一个段落,其red
字体大小为1.25rem
.
外面的所有段落alpha-paragraph
都不会是红色的,因此您可以独立设置其他 CSS 样式。
Step 3. 结合模板标签#
我们可以使用 HTML 标签代替将我们的 Web 组件硬编码到 Javascript 中。HTML 有两个我们可以在这里使用的标签,template
和slot
.
模板标签是指整个 Web 组件,而插槽是该模板的一小部分,可以更改。模板标签的示例如下所示:
<template id="alphaParagraph"> <slot name="paragraph-text"><p>Default Text</p></slot> <p>Another, unchangeable paragraph</p> </template>
第 2 行的插槽是指我们可以更改的内容。在我看来,我们真的不需要使用模板标签,但插槽很有用。我们可以alpha-paragraph
通过更改 innerHTML 来更新我们的插槽:
class Paragraph extends HTMLElement { constructor() { super() // Attach shadow DOM let shadow = this.attachShadow({mode: 'open'}); // Append our Paragraph shadow.innerHTML = ` <slot name="paragraph-text"><p>Default Text</p></slot> <p>Another, unchangeable paragraph</p> `; // Add in our CSS let style = document.createElement('style'); let elCss = style.textContent = ` p { color: red; font-size: 1.25rem; } `; // Append our CSS shadow.appendChild(style); } } customElements.define('alpha-paragraph', Paragraph);
然后我们可以使用 HTML 中的 ‘slot’ 属性将插槽替换为我们自己的自定义元素。在下面的示例中,整个槽被替换为包含文本“自定义文本”的段落元素。
<alpha-paragraph color="blue"> <p slot="paragraph-text"> Custom Text </p> </alpha-paragraph>
步骤 4. 属性#
由于所有 HTML 元素都可以具有属性,因此我们可以使用该getAttribute
函数访问它们。因此,我们可以轻松地重写我们的代码以获得自定义颜色:
class Paragraph extends HTMLElement { constructor() { super() // Attach shadow DOM let shadow = this.attachShadow({mode: 'open'}); // Append our Paragraph shadow.innerHTML = ` <slot name="paragraph-text"><p>Default Text</p></slot> <p>Another, unchangeable paragraph</p> `; // Our custom color let color = 'red'; if(this.getAttribute('color') !== null) { color = this.getAttribute('color'); } // Add in our CSS let style = document.createElement('style'); let elCss = style.textContent = ` p { color: ${color}; font-size: 1.25rem; } `; // Append our CSS shadow.appendChild(style); } } customElements.define('alpha-paragraph', Paragraph);
<alpha-paragraph color="blue"> <p slot="paragraph-text"> Custom Text </p> </alpha-paragraph>
通过将其全部放入我们的 Javascript 中,我们确保我们可以轻松地将这个 Web 组件导入其他地方。下面是我们最终 Web 组件的演示:
<alpha-paragraph> <p slot="paragraph-text"> Custom Text </p> </alpha-paragraph>
p { color: blue; }
class Paragraph extends HTMLElement { constructor() { super() // Attach shadow DOM let shadow = this.attachShadow({ mode: 'open' }); // Append our Paragraph shadow.innerHTML = ` <slot name="paragraph-text"><p>Default Text</p></slot> <p>Another, unchangeable paragraph</p> `; // Add in our CSS let style = document.createElement('style'); let elCss = style.textContent = ` p { color: red; font-size: 1.25rem; } `; // Append our CSS shadow.appendChild(style); } } customElements.define('alpha-paragraph', Paragraph);