Web Components之自定義HTML標籤

一、Web Components

  Web Components是一種組件化的方式,它允許您創建可重用的定製元素(它們的功能封裝在您的代碼之外)並且在您的web應用中使用它們。Web Components的一個重要特性是封裝,通過Web Components創建的組件,可以有效地將其自身的html結構、css樣式及行爲隱藏起來,並從頁面中的其他代碼分離出來,也就是每個組件都是獨立的,不會互相干擾。下面是一個Web Components的簡單應用:

<custom-button text="自定義按鈕"></custom-button>

在這裏插入圖片描述
  從上面可以看出我們通過自定義了一個custom-button標籤,讓按鈕擁有了一些自定義的樣式,可以看出這種方式其實就是組件化的思想。那麼這樣一個自定義標籤如何實現的呢?

二、自定義標籤

  W3C制定了兩種custom elements: Autonomous custom elementsCustomized built-in elements

2.1 Autonomous custom elements

  獨立的元素,這種元素不會繼承現有內建的HTML標籤。你可以通過<custom-button></custom-button>document.createElement('custom-button')來進行使用。
  下面我們來實現上面的小例子,實現這樣一個例子很簡單,W3C提供了一個HTMLElement類,我們通過繼承該類即可實現我們自定義的標籤:

class CustomButton extends HTMLElement{
  constructor() {
    super()
    
    // 創建一個shadow root
    const shadow = this.attachShadow({mode: 'open'})
    
    // 創建一個button
    const button = document.createElement('button')
    button.setAttribute('class', 'custom-button')
    
    const text = this.getAttribute('text')
    button.textContent = text
    
    // 創建樣式
    const style = document.createElement('style')
    style.textContent = `
      .custom-button{
        border: none;
        cursor: pointer;
        background: hsla(130, 70%, 50%, 1);
        color: #ffffff;
        line-height: 30px;
        border-radius: 5px;
      }
    `
    
    // 將創建的元素添加到shadow dom中
    shadow.appendChild(style)
    shadow.appendChild(button)
  }
  
}

// 註冊自定義的標籤
customElements.define('custom-button', CustomButton)

2.1.1 Shadow DOM

  可以看出上面代碼在繼承HTMLElement之後,我麼執行了this.attachShadow({mode: 'open'}),該行是創建一個Shadow Dom,那麼Shadow Dom是個什麼東西呢?Shadow Dom是每個標籤之間不會互相干擾的關鍵所在,它可以將一個隱藏的、獨立的DOM添加到一個元素上。操作Shadow Dom與常規的Dom沒有任何區別——例如添加子節點、設置屬性,以及爲節點添加自己的樣式,或者爲整個 Shadow DOM添加樣式(例如在<style> 元素內添加樣式)。不同的是,Shadow DOM內部的元素始終不會影響到它外部的元素,這爲封裝提供了便利。

  • Shadow DOM 基本用法
      可以使用Element.attachShadow()方法來創建一個Shadow Dom,它可以被添加到任何一個元素中。它接受一個配置對象作爲參數,該對象有一個mode屬性,值可以是open或者closed
let shadow = this.attachShadow({mode: 'open'});
let shadow = this.attachShadow({mode: 'closed'});

  open表示你可以通過頁面內的 JavaScript 方法來獲取Shadow DOM,例如上面例子:

let shadow = document.querySelector('custom-button').shadowRoot
console.log(shadow) // 此處shadow就是shadow dom下的所有子節點;如果爲‘closed’,則爲null

2.2 Customized built-in elements

  該類型元素是用過繼承html中內建的元素進行自定義。例:

class CustomButton2 extends HTMLButtonElement{
  constructor () {
    super()
    this.style = `
    border: none;
        cursor: pointer;
        background: hsla(130, 70%, 50%, 1);
        color: #ffffff;
        line-height: 30px;
        border-radius: 5px;
    `
  }
}

customElements.define('custom-button2', CustomButton2, {extends: 'button'})

  注意這裏繼承的不再是HTMLElement,而是HTMLButtonElement,當前你也可以繼承HTMLSpanElementHTMLInputElement等,註冊時需要添加第三個參數,該參數指明我們需要繼承的元素。在使用時,通過<button is="custom-button2">ddd</button>document.createElement('button', {is: 'custom-button2'})

三、<template>與<slot>

3.1 <template>

  當網頁上重複使用相同的標記結構時,使用某種模板而不是一遍又一遍地重複相同的結構是有意義的。以前這是可行的,但HTML<template> 元素使它更容易實現。 此元素及其內容不會在DOM中呈現,但仍可使用JavaScript去引用它。

<template id="template">
  <p>template</p>
</template>
const template = document.querySelector('#template')
const content = template.content
document.body.appendChild(content)

  在2.1的例子中我們的Shadow Dom都是在js中拼接的,下面我們將定義的Shadow Dom定義在template中,將其從js中分離出來:

<template id="button-template">
    <style>
      .custom-button{
        border: none;
        cursor: pointer;
        background: hsla(130, 70%, 50%, 1);
        color: #ffffff;
        line-height: 30px;
        border-radius: 5px;
      }
    </style>
    <button class="custom-button"></button>
  </template>
class CustomButton extends HTMLElement{
  constructor() {
    super()
    const shadow = this.attachShadow({mode: 'open'})
    const template = document.querySelector('#button-template')
    const text = this.getAttribute('text')
    const append = template.content.cloneNode(true)
    append.children[1].innerHTML = text
    shadow.appendChild(append)
  }
}

customElements.define('custom-button', CustomButton)

3.2 <slot>

  插槽,作用於vue中的slot相同,可在Web Components中插入DOM結構,下面使用slot修改一下上面的按鈕(能被插入到槽中的元素視爲 Slotable; 稱已經插入到槽中的元素爲slotted):

<custom-button3 class="xxx">
	<a slot="my-content" >slot插槽</a>
</custom-button3>
class CustomButton3 extends HTMLElement{
  constructor() {
    super()
    const template = document.querySelector('#button-template').content
    this.attachShadow({mode: 'open'}).appendChild(template.cloneNode(true))
  }
}
customElements.define('custom-button3', CustomButton3)

  關於Slotable的兩個屬性:

// 獲取slot的名字
document.querySelector('custom-button3 a').slot
// 獲取Slotable
document.querySelector('custom-button3 a').assignedSlot
// 獲取slotted節點
document.querySelector('custom-button3 a').assignedSlot.assignedElements()

示例:https://github.com/MAXLZ1/web-component/tree/master

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章