web componet 是在原生JS中自定义复用组件的技术。
主要包括三种技术模块:自定义元素、影子DOM、HTML模板。
自定义组件
通过注册自定义元素Element
,CustomElementRegistry
接口提供方法、属性,并返回元素的信息。
通过全局属性customElements
访问接口提供的方法、属性
.define(Name,Class,**options)
:
- 自定义元素名称符合标准字符串,且不能是单个单词,需要使用短横线连接。
- 用于定义元素行为的类。
- 可选参数。包含
extends
配置对象,指定自定义元素继承的内置元素。
.get(Name)
: 获取已定义元素的构造函数。
.upgrade(Root)
:更新节点子树中包含所有阴影DOM的自定义元素。
whenDefined(Name)
: 自定义元素被定义时返回一个Promise
;
生命周期回调函数处理一些业务:
connectedCallback
自定义元素被插入文档DOM时,调用。disconnectedCallback
自定义元素从文档删除时,调用。adoptedCallback
自定义元素被移动到新的文档时,调用。attributeChangedCallback
自定义元素增加、删除、修改属性时 ,调用。触发此回调函数,必须监听变化的属性
static get observedAttributes()
,返回监听的属性。
独立元素
继承自HTMLElement
, 通过this
调用元素的属性、方法。
自定义元素使用:
<custom-word text="自定元素">
<h3>自定义元素</h3>
</custom-word>
自定义元素定义:
class CustomeWord extends HTMLElement{
static get observedAttributes(){
return ["text","src"];
}
constructor(){
super();
// 属性更改
// this.setAttribute("text","更新属性'text'");
}
connectedCallback(){
console.info("元素插入到DOM");
// 属性更新
this.setAttribute("src","更新属性'text'");
// DOM删除
this.remove();
}
disconnectedCallback(){
console.info("元素从DOM移除");
}
adoptedCallback(){
console.info("元素移动到新的DOM");
}
attributeChangedCallback(name,oldValue,newValue){
console.info("元素属性发生添加、删除、更新");
}
}
// let customElement = new CustomElementRegistry();
customElements.define("custom-word",CustomeWord);
测试结果图:
继承内置元素
.define()
接受到的第三个参数,可设置继承内置元素。
is
属性,定义内置元素表现得像自定义元素。
每一个内置元素都有对应的类型接口,以保持在浏览器内原有的语义。
自定义元素使用:通过is
属性绑定自定义名称,实现自定义行为。
<ul is="custom-ul">
<li>one</li>
<li>two</li>
</ul>
class CustomeUl extends HTMLUListElement{
constructor(){
super();
}
}
customElements.define("custom-ul",CustomeUl,{
extends:"ul"
});
影子DOM
可以创建一个独立的区域,将元素属性、结构、行为封装,隐藏起来。与页面其他部分分开。
原自定义元素结构:
使用shadow DOM
后,结构:
Element.attachShadow()
方法可以讲一个shadow root 附加到一个元素上。
Shadow Dom内部的元素不会影响到它外部的元素(除了::focus-within
)
接受一个配置参数mode
:
open
标识可以通过页面内的JavaScript获取到Shadow DOM.// 外部获取shadow root let customElement = document.getElementsByTagName("custom-word"); let shadowDom = customElement[0].shadowRoot;
class CustomeWord extends HTMLElement{
constructor(){
super();
// 属性更改
// this.setAttribute("text","更新属性'text'");
// 附加到shadow root
let shadow = this.attachShadow({mode:"open"});
// 获取属性并加到元素内
let text = this.getAttribute('text');
let info = document.createElement('span');
info.textContent = text;
// 附加一些样式
let style = document.createElement('style');
style.textContent = `
span{
color:red;
font-size:24px
}
`;
shadow.appendChild(info);
shadow.appendChild(style);
}
}
customElements.define("custom-word",CustomeWord);
HTML模板
定义模板内容,重复使用。
template
标签
<template>
元素结构内容,页面加载后不会呈现。可以通过属性content
获取到内部内容结构。
<template id="custom-template">
<style>
p{
color:yellow;
font-weight: bold;
text-decoration: underline;
}
</style>
<p>使用`template`定义模板内容</p>
</template>
class CustomeWord extends HTMLElement{
constructor(){
super();
// 附加到shadow root
let shadow = this.attachShadow({mode:"open"});
// 使用template 标签定义内容
let template = document.querySelector("#custom-template");
shadow.appendChild(template.content.cloneNode(true));
}
}
customElements.define("custom-word",CustomeWord);
slot
插槽标签
有其属性name
属性定义,在其中模板中定义占位符。
<custom-word text="自定元素">
<details slot="description">
<header>详情描述</header>
<br>
<p>使用`slot`定义占位,通过属性`name`建立关联性。渲染后通过`slot`代替模板位置中的内容。</p>
</details>
</custom-word>
<!--定义模板内容-->
<template id="custom-template">
<style>
p{
color:yellow;
font-weight: bold;
text-decoration: underline;
}
</style>
<p>使用`template`定义模板内容</p>
</template>
使用slot
定义占位,通过属性name
建立关联性。渲染后通过slot
代替模板位置中的内容。
class CustomeWord extends HTMLElement{
constructor(){
super();
// 附加到shadow root
let shadow = this.attachShadow({mode:"open"});
// 使用template 标签定义内容
let template = document.querySelector("#custom-template");
shadow.appendChild(template.content.cloneNode(true));
}
}
customElements.define("custom-word",CustomeWord);
渲染后结果: