一、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 elements
、Customized 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
,當前你也可以繼承HTMLSpanElement
,HTMLInputElement
等,註冊時需要添加第三個參數,該參數指明我們需要繼承的元素。在使用時,通過<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()