Javascript肆-Web Components

0

肆-Web Components

阅读:789 时间:2022年11月11日

整体介绍 用过Vue, React等现代前端框架的,对此概念会很熟悉: Web Components 是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的 web 应用中使用它们。 与框架不同的是,它是原生,无任何依赖的组...

整体介绍

用过Vue, React等现代前端框架的,对此概念会很熟悉:

Web Components 是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的 web 应用中使用它们。

与框架不同的是,它是原生,无任何依赖的组件方案。

先来看下Web Components的三个要素。

一、Custom elements(自定义元素)

一组 JavaScript API,允许定义 custom elements 及其行为,然后可以在用户界面使用

下面是一个简单的示例:

UserCard.js

class UserCard extends HTMLElement {
  constructor() {
    super()
    const container = document.createElement('div')
    const p = document.createElement('h1')
    p.innerHTML = this.getAttribute('name')
    container.appendChild(p)
    this.appendChild(container)
  }
}
window.customElements.define('user-card', UserCard)

demo.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>Document</title>
</head>
<body>
<user-card name="Lenton"></user-card>
<script src="./UserCard.js"></script>
</body>
</html>

浏览中效果如下:

二、Shadow DOM(影子 DOM)

一组 JavaScript API,用于将封装的“影子”DOM 树附加到元素(与主文档 DOM 分开呈现)并控制其关联的功能。通过这种方式,可以保持元素的功能私有,可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。

我们对上例的js做下改写:

class UserCard extends HTMLElement {
  constructor() {
    super()
    const shadow = this.attachShadow({mode:  'closed'})
    const container = document.createElement('div')
    const p = document.createElement('h1')
    p.innerHTML = this.getAttribute('name')
    container.appendChild(p)
    shadow.appendChild(container)
  }
}

window.customElements.define('user-card', UserCard)

三、HTML templates(HTML 模板)

<template> 和 <slot> 元素使您可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用

如果所有dom结构都在JS类里用createElement创建,也太麻烦。template的出现就是为了解决这个问题。

继续改写示例:

demo.html, UserCard.js

<!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>Document</title>
</head>
<body>
<template id="userCard">
  <h1>Lenton</h1>
  <p>lenton@vip.qq.com</p>
</template>
<user-card name="Lenton"></user-card>
<script src="./UserCard.js"></script>
</body>
</html>
class UserCard extends HTMLElement {
  constructor() {
    super()
    const shadow = this.attachShadow({mode: 'closed'})
    const template = document.querySelector('#userCard')
    const content = template.content.cloneNode(true)
    shadow.appendChild(content)
  }
}
window.customElements.define('user-card', UserCard)

文章以上部分就是对Web Components的整体简要介绍。有了对整体的大至了解后。就要开始正式学习了。

正式开始

一 、使用 custom elements

一个 custom element 的类对象可以通过 ES 2015 标准里的类语法生成

class UserCard extends HTMLElement {
  constructor() {
    super()
    // 实现代码
  }
}

使用时必需要注册。指定tag名称与关联的类。

注意: tag名称必须是以短横杠“-“分隔的单词,用于区分原生html tag.

window.customElements.define('user-card', UserCard)

然后添加组件内容:

添加一个h1标签,显示名称

添加一个p标签,显示邮箱

从组件属性中获取值并填充到相应位置

class UserCard extends HTMLElement {
  constructor() {
    super()
    // const shadow = this.attachShadow({mode: 'closed'})
    const container = document.createElement('div')
    const name = document.createElement('h1')
    const email = document.createElement('p')
    container.appendChild(name)
    container.appendChild(email)
    // shadow.appendChild(container)
    name.innerHTML = this.getAttribute('name') ?? '-'
    email.innerHTML = this.getAttribute('email') ?? '-'
    this.appendChild(container)
  }
}
window.customElements.define('user-card', UserCard)

html部分:

<user-card name="Lenton" email="lenton@vip.qq.com"></user-card>

生命周期回调函数

在 custom element 的构造函数中,可以指定多个不同的回调函数,它们将会在元素的不同生命时期被调用

connectedCallback:当 custom element 首次被插入文档 DOM 时,被调用。

disconnectedCallback:当 custom element 从文档 DOM 中删除时,被调用。

adoptedCallback:当 custom element 被移动到新的文档时,被调用。

attributeChangedCallback: 当 custom element 增加、删除、修改自身属性时,被调用。

改写代码,组件内显示名称随属性name值改变时改变

class UserCard extends HTMLElement {
  constructor() {
    super()
    const container = document.createElement('div')
    const name = document.createElement('h1')
    const email = document.createElement('p')
    container.appendChild(name)
    container.appendChild(email)
    name.innerHTML = this.getAttribute('name') ?? '-'
    email.innerHTML = this.getAttribute('email') ?? '-'
    this.appendChild(container)
  }

  static get observedAttributes() {
    return ['name', 'email'];
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'name') {
      this.querySelector('h1').innerHTML = newValue
    }
  }
  
}
window.customElements.define('user-card', UserCard)

现在页面如下:

在控制台执行一条改变name的脚本

document.querySelector('user-card').setAttribute('name', 'Alina')

可以看到页面变化

添加样式

可以直接在组件内添加style标签来添加样式。

样式示例:

class UserCard extends HTMLElement {
  constructor() {
    super()
    const container = document.createElement('div')
    const name = document.createElement('h1')
    const email = document.createElement('p')
    container.appendChild(name)
    container.appendChild(email)
    name.innerHTML = this.getAttribute('name') ?? '-'
    email.innerHTML = this.getAttribute('email') ?? '-'
    this.appendChild(container)
    this.updateStyle()
  }

  updateStyle() {
    let style = this.querySelector('style')
    if (!style) {
      style = document.createElement('style')
      this.appendChild(style)
    }
    style.textContent = `
      h1{
        color: green;
      }
      p{
        color: blue;
      }
    `
  }
  
}
window.customElements.define('user-card', UserCard)

页面效果如下

Shadow DOM

使用Shadow DOM示例

class UserCard extends HTMLElement {
  constructor() {
    super()
    const shadow = this.attachShadow({mode: 'closed'})
    const container = document.createElement('div')
    const h1 = document.createElement('h1')
    h1.innerHTML = 'Lenton'
    container.appendChild(h1)
    shadow.appendChild(container)
  }
}
window.customElements.define('user-card', UserCard)

HTML templates

有两个要知道的部分:

<template> 包含一个 HTML 片段,不会在文档初始化时渲染。但是可以在运行时使用 JavaScript 显示。主要用作自定义元素结构的基础。

<slot> 一个占位符,你可以填充自己的标记,这样你就可以创建单独的 DOM 树并将它们呈现在一起。

先看一个示例:

demo.html, UserCard.js

<!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>Document</title>
</head>
<body>
<template id="userCard">
  <h1>无名称</h1>
  <slot></slot>
  <p>无邮箱</p>
</template>
<user-card name="Lenton">
  <p>这是<code>slot</code>的填充内容</p>
</user-card>
<script src="./UserCard.js"></script>
</body>
</html>
class UserCard extends HTMLElement {
  constructor() {
    super()
    const shadow = this.attachShadow({mode: 'closed'})
    const template = document.querySelector('#userCard')
    const content = template.content.cloneNode(true)
    shadow.appendChild(content)
    shadow.querySelector('h1').innerHTML = this.getAttribute('name')
  }
}
window.customElements.define('user-card', UserCard)

页面效果如下:

可以看到,会使用指定的template内容来生成组件。

同时,组件标签内的内容,会被插入到<template>的<slot>位置内。

未完待续...

发表评论说说你的看法吧

精品模板蓝瞳原创精品网站模板

^