WebComponents是一套不同的技术,可以创建可重用的定制元素,可以在web应用中使用它们。

一个重要属性就是封装,可以将标记结构,样式和行为隐藏起来,并与页面上的其他代码相隔离,保证不会混在一起,可使代码更加干净,整洁

web-componetns 有三项主要技术组成,可以用来一起使用来创建、封装定制的元素。

  • Custom elements - 自定义元素,允许自定义custom elements及其行为,然后可以在用户界面中按照需求来使用
  • Shadow Dom - 用于将封装的影子dom树附加到元素并控制其关联的功能。可以保持元素的功能私有,这样它们就可以被校本化和样式化,而不用担心与文档的其他部分发生冲突
  • HTML templates - 有template和slot元素可以编写不在呈现页面中显示的标记模版。可被多次重用

    Web Component

    1. <web-card></web-card>
    2. <script>
    3. customElements.define("web-card", class extends HTMLElement {
    4. constructor() {
    5. super()
    6. let title = document.createElement("h1");
    7. title.textContent = "Web Components";
    8. this.append(title);
    9. }
    10. })
    11. </script>

    image.png
    以上代码定义了一个简单的html标签

    获取属性

    <web-card data-content="web-card" list="[1,2,3,4]"></web-card>
      this.dataset.content // web-card
    this.getAttribute("list") //[1,2,3,4]
    

    添加事件处理器

    customElements.define("web-card", class extends HTMLElement {
      constructor() {
        super()
        console.dir(this);
        console.log(this.getAttribute("list"));
        let title = document.createElement("h1");
        title.textContent = "Web Components";
        this.addEventListener("click",this.onClick);
        this.append(title);
      }
    
      onClick = () => {
        console.log("clicked");
      }
    })
    

    生命周期函数

  • connectedCallback: 当自定义元素第一次被连接到文档DOM时被调用。

  • disconnectedCallback: 当自定义元素与文档DOM断开连接时被调用。
  • adoptedCallback: 当自定义元素被移动到新文档时被调用。
  • attributeChangedCallback: 当自定义元素的一个属性被增加、移除或更改时被调用。 ```javascript class Card extends HTMLElement { constructor() { super() } connectedCallback() { console.log(“connectedCallback”); } disconnectedCallback() { console.log(“disconnectedCallback”); } adoptedCallback() { console.log(“adoptedCallback”); }

    // 一个镜头属性,返回组件需要检测的属性,属性值在变化的时候毁掉函数才会被执行 static get observedAttributes() {return [ ; }

    attributeChangedCallback() { console.log(“attributeChangedCallback”); }

}

<a name="sExSQ"></a>
### CustomElementRegistry

1. define - 定义一个新元素,第三个参数为一个选项
1. upgrade - 将更新节点子树中所有包含阴影的自定义元素。将一个自定义标签升级成可使用的标签
1. whenDefined - 查询一个自定义标签,当元素被定义时返回一个promise
1. get - 返回指定自定义元素的构造函数,如果未定义返回undefined
```javascript
 setTimeout(() => {
      customElements.define("web-card", class extends HTMLElement {
      constructor() {
        super()
        let title = document.createElement("h1");
        title.textContent = "Web Components";
        this.append(title);
      }
    },{});
    },3000);

//     在三秒以后,webCard被定义,promise返回
customElements.whenDefined("web-card").then(() => {
  console.log('元素被定义');
});

Shadow Dom

像是影子一样,在文档中默认不会展示原有的html内容,会展示dom结构树中已经定义的内容

<proto-card></proto-card>

<script>
class Proto extends HTMLElement {
  constructor() {
    super()
    const dom = this.attachShadow({ mode: "open" })
    const p = document.createElement("p")
    p.textContent = "shadow dom"
    p.addEventListener("click",() =>  console.log("p clicked"))
    dom.append(p)
  }
}
customElements.define("proto-card", Proto)
</script

image.png

HTML Template

模版元素使一种保存客户端内容机制,该内容在加载页面时不会呈现。可以在运行时进行实例化

将模版视为一个可存储在文档中以便后续使用的内容片段。

模版中定义的内容存储在content属性中。 该属性是只读的

 <template id="temp">
    <style>
      .box {
        border: 1px solid red;
      }
      .box > h3 {
        color: royalblue;
      }
      .box > p {
        color: slategray;
        font-size: 18px;
      }

    </style>
    <div class="box">
      <h3>title</h3>
      <p>content</p>
    </div>
  </template>
<script>
 // 要cloneNode(true) 表示深克隆,不然模版只能使用一次
dom.append(temp.content.cloneNode(true)); // 在shadow dom 中使用
this.append(temp.content.cloneNode(true)); // 在自定义元素中使用
</script>

效果
image.png

样式的隔离


<style type="text/css">
  .box {
    background:  red;
  }
</style>


<div class="box">
  bod top
</div>

<template id="app">
  <style>
    .box {
      background: blue;
    }
  </style>
</template>

<script type="text/javascript">

class Proto extends HTMLElement {
  constructor() {
    super()
    const dom = this.attachShadow({ mode: "open" });
    const p = document.createElement("p");
    p.textContent = "shadow dom";
    p.classList.add("box");
    dom.append(document.querySelector("#app").content.cloneNode(true));
    dom.append(p);

    window.foo = "true";
  }
}
customElements.define("proto-card", Proto);

</script>

<proto-card ></proto-card>


<script type="text/javascript">
  console.log(foo)
</script>

image.png