优点:原生组件,不需要框架,性能好代码少。
缺点:兼容性问题
组件化好处: 高内聚、可重用、可组合WebComponent
和
元素使您可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。组件的名字必须以中划线分割,避免与native标签冲突
折叠分栏组件
1、组件使用
nodejs welcome
react welcome
vue welcome
2、定义组件模板
3、 引入组件
4、 注册组件
window.customElements.define('zf-collapse',Collapse);
window.customElements.define('zf-collapse-item',CollapseItem);
用于注册组件
import Collapse from './collapse.js';
import CollapseItem from './collapse-item.js';
window.customElements.define('zf-collapse',Collapse);
window.customElements.define('zf-collapse-item',CollapseItem);
// 设置组件默认显示的状态
let defaultActive = ['1','2']; // name:1 name:2 默认展开 3 应该隐藏
document.querySelector('zf-collapse').setAttribute('active',JSON.stringify(defaultActive));
// 每个item需要获取到defaultActive 和自己的name属性比较,如果在里面就显示,不在里面就隐藏
document.querySelector('zf-collapse').addEventListener('changeName',(e)=>{
let {isShow,name} = e.detail;
if(isShow){
let index = defaultActive.indexOf(name);
defaultActive.splice(index,1);
}else{
defaultActive.push(name);
}
document.querySelector('zf-collapse').setAttribute('active',JSON.stringify(defaultActive));
});
// shadowDOM 完全隔离
// 组件间的通信 属性,事件
// customEvent -> webcomponent 兼容性差,没有自动更新机制
defaultActive是初始展开的name
一开始把defaultActive设置在zf-collapse dom上的active属性 后续传递给子组件使用
监听changeName事件 再改变active属性
继承HTMLElement
1)先调用this.attachShadow({ mode: 'open' })激活shadowDOM
2)拿到组件模板dom 拷贝一份
let cloneTemplate = tmpl.content.cloneNode(true);
3) 定义style style.textContent ...
4) dom和 style 放在shadow
shadow.appendChild(style);
shadow.appendChild(cloneTemplate);
5) 监控slot变化 插槽有值时 记录this.slotList (子组件记录)
执行 render是为了给子组件 改变active
slot.addEventListener('slotchange', (e) => {
this.slotList = e.target.assignedElements();
this.render();
})
observedAttributes 监听active属性变化
attributeChangedCallback 属性变化回调 重新赋值 this.activeList, 执行this.render
collapse.js主要记录active属性变化 并传给子组件
class Collapse extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
const tmpl = document.getElementById('collapse_tmpl');
let cloneTemplate = tmpl.content.cloneNode(true);
let style = document.createElement('style');
// :host 代表的是影子的根元素
style.textContent = `
:host{
display:flex;
border:3px solid #ebebeb;
border-radius:5px;
width:100%;
}
.zf-collapse{
width:100%;
}
`
shadow.appendChild(style);
shadow.appendChild(cloneTemplate);
let slot = shadow.querySelector('slot'); // 监控slot变化
slot.addEventListener('slotchange', (e) => {
this.slotList = e.target.assignedElements();
this.render();
})
}
static get observedAttributes() { // 监控属性的变化
return ['active']
}
// update
attributeChangedCallback(key, oldVal, newVal) {
if (key == 'active') {
this.activeList = JSON.parse(newVal);
this.render();
}
}
render() {
if (this.slotList && this.activeList) {
[...this.slotList].forEach(child => {
child.setAttribute('active', JSON.stringify(this.activeList))
});
}
}
// connectedCallback(){
// console.log('插入到dom时执行的回调')
// }
// disconnectedCallback(){
// console.log('移除到dom时执行的回调')
// }
// adoptedCallback(){
// console.log('将组件移动到iframe 会执行')
// }
}
export default Collapse
6、CollapseItem组件定义
class CollapseItem extends HTMLElement {
constructor() {
super();
let shadow = this.attachShadow({ mode: 'open' });
let tmpl = document.getElementById('collapse_item_tmpl');
let cloneTemplate = tmpl.content.cloneNode(true);
let style = document.createElement('style');
this.isShow = true; // 标识自己是否需要显示
style.textContent = `
:host{
width:100%;
}
.title{
background:#f1f1f1;
line-height:35px;
height:35px;
}
.content{
font-size:14px;
}
`
shadow.appendChild(style)
shadow.appendChild(cloneTemplate);
this.titleEle = shadow.querySelector('.title');
this.titleEle.addEventListener('click',()=>{
// 如果将结果传递给父亲 组件通信?
document.querySelector('zf-collapse').dispatchEvent(new CustomEvent('changeName',{
detail:{
name:this.getAttribute('name'),
isShow:this.isShow
}
}))
})
}
static get observedAttributes() { // 监控属性的变化
return ['active', 'title', 'name']
}
// update
attributeChangedCallback(key, oldVal, newVal) {
switch (key) {
case 'active':
this.activeList = JSON.parse(newVal); // 子组件接受父组件的数据
break;
case 'title':
this.titleEle.innerHTML = newVal; // 接受到title属性 作为dom的title
break;
case 'name':
this.name = newVal
break;
}
let name = this.name;
if (this.activeList && name) {
this.isShow = this.activeList.includes(name);
this.shadowRoot.querySelector('.content').style.display = this.isShow ? 'block' : 'none'
}
}
}
export default CollapseItem
title dom 定义click事件, 向zf-collapse dom元素触发 自定义事件changeName, 传 name isShow,
observedAttributes 观察3个属性变化
attributeChangedCallback 属性变化后的回调
this.activeList: 父组件的传的参数
title是该组件的title
name是该组件name
如果name在this.activeList里面 就是展示 显示样式block 即可。
顶层组件传 activeList 子组件判断 展示就行了