web Components (一)

103 阅读2分钟

前言:本文纯作者个人理解来讲述什么是web Components,说的不对的地方请指正。作者学习web components是为了学习MicroApp微前端。

webComponents的主要组成

1、Custom elements (自定义元素) 

2、Shadow Dom (影子dom)

3、Html template (Html 模版)

 

一、自定义元素

白话文理解:我可以自定义一些html标签,但是这些标签的名称必须包含连词线,用来区分原生html元素。例如 <user-info></user-info> 不能写成 <userInfo></userInfo>

user-info 标签的具体实现

class UserInfo extends HTMLElement {
    constructor(){
       super();
       this.attachShadow( { mode: 'open' } );
        
    }
    
    connectedCallback(){
        // 生命周期函数 当自定义元素首次被插入文档 DOM 时,被调用
        const container = document.createElement('div');
        container.classList.add('test');
        container.innerHTML=`<p class="name">Bain</p><p class="age">12</p>`
        this.shadowRoot.append(container);
    }
    disconnectedCallback(){
        //当自定义元素从文档dom中删除时被调用
    }
    
    adoptedCallback(){
        // 当自定义元素被移动到新文档时被调用
    }
    attributeChangedCallback(){
        // 当自定义元素 增加、删除、修改自身属性时被调用
    }
}
告诉浏览器<user-info>元素与这个类关联
window.customElements.define('user-info',UserInfo)

 

二、影子dom 

白话文理解:我不想让别人获取user-info 的shadowdom。这部分 DOM 默认与外部 DOM 隔离,内部任何代码都无法影响外部,外部代码也无法影响内部(例如:全局样式之类,获取dom节点之类)。这个就叫做影子dom 。

实现方式:

 this.attachShadow({mode:'open'})

具体现象如下:

document.querySelector('.test'); // null 无法获取testdom节点
// 全局设置 test样式无效

 

三、template标签

白话文理解:在js中写dom会比较麻烦,所以使用template标签定义dom

上文可以改写为:

<template id="userInfoTemplate">
<div class="container">
 <p class="name">Bain</p>
    <p class="age">12</p>
  </div>
</template> 

class UserInfo extends HTMLElement {
    constructor(){
        super();
        var shadow = this.attachShadow( { mode: 'open' } );
        const ele = document.getElementById('userInfoTemplate');
        const content = ele.content.cloneNode(true);
        shadow.append(content);
    }

}

自定义元素样式实现

:host{
    // 表示当前自定义元素 <user-info></user-info> 自定义元素默认是行内元素
    display: block;
}
:host([propertyName]){
    // 自定义元素存在 propertyName 属性
    background:red;
}
:host(.test){
    // 自定义元素存在 test类名
    background: black;
}

自定义元素通信

// html
<script type='module' src='./main.js' ></script>
<body>
    <customer-father></customer-father>
</body>

// main.js
import CustomerFather form './father.js';

// father.js
import CustomerSon from './son.js'
class CustomerFather extends HTMLElement {
    constructor(){
        super();
        this.attachShadow( { mode: 'open' } );
    }

    connectedCallback(){
        this.render()
    }

    render(){
        const ele = document.createElement('div');
        ele.innerHTML=`<customer-son></customer-son>`
        this.shadowRoot.append(ele)
    }

    change(){
        console.log('执行CustomerFather')
        // 执行子元素方法
        this.shadowRoot.querySelector('customer-son').change();
    }
}
window.customElements.define('customer-father',CustomerFather)
export default CustomerFather;

// son.js
class CustomerSon extends HTMLElement {
    constructor(){
        super();
        this.attachShadow( { mode: 'open' } );
    }

    connectedCallback(){
        this.render()
    }

    render(){
        const ele = document.createElement('div');
        ele.innerText=`我是子元素`
        ele.onclick = ()=>{
            console.dir(this.getRootNode())
            this.getRootNode().host.change();
        }
        this.shadowRoot.append(ele)
    }

    change(){
        console.log('执行CustomerSon')
    }
}
window.customElements.define('customer-son',CustomerSon)
export default CustomerSon;