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
独立的元素,这种元素不会继承现有内建的HTML标签。你可以通过
或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)
可以看出上面代码在继承HTMLElement
之后,我么执行了this.attachShadow({mode: 'open'})
,该行是创建一个Shadow Dom
,那么Shadow Dom
是个什么东西呢?Shadow Dom
是每个标签之间不会互相干扰的关键所在,它可以将一个隐藏的、独立的DOM添加到一个元素上。操作Shadow Dom
与常规的Dom
没有任何区别——例如添加子节点、设置属性,以及为节点添加自己的样式,或者为整个 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
该类型元素是用过继承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
等,注册时需要添加第三个参数,该参数指明我们需要继承的元素。在使用时,通过或
document.createElement('button', {is: 'custom-button2'})
。
当网页上重复使用相同的标记结构时,使用某种模板而不是一遍又一遍地重复相同的结构是有意义的。以前这是可行的,但HTML 元素使它更容易实现。 此元素及其内容不会在DOM中呈现,但仍可使用JavaScript去引用它。
<template id="template">
<p>templatep>
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)
插槽,作用于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