一文读懂Web Component

Web Component组件

前言:Web Component不是新东西,是几年前的技术,vue等前端框架就是借鉴的Web Component,但是受限于浏览器兼容性,一直没有大规模应用在项目里,直到现在(2018年年末),除IE仍不支持之外,其它主流浏览器都支持Web Component。

一、什么是组件

一个组件就是对局部视图(html、css、js)的一个封装

Web Component不是一个东西,它分为四部分,分别是:

template——模板:

script模板是通过获取script的innerHTML来获取,template则是获取读取template节点的content属性来获取

// 获取模板内容
console.log(document.querySelector('template').content);
HTML import——HTML导入
Shadow DOM——影子DOM:

Web Component 允许内部代码隐藏起来,这叫做 Shadow DOM,即这部分 DOM 默认与外部 DOM 隔离,内部任何代码都无法影响外部。
Vue通过虚拟Dom 和双向绑定实现渲染, Polymer通过shadowDom和双向绑定实现渲染

Custom Elements——自定义元素

二、组件的定义方式

  1. 定义HTML模板
  2. 定义CSS样式
  3. 定义javaScript行为

三、组件的使用方式

大致结构:

  • 在body里使用组件,如 < user-card> < /user-ard >
  • 在body里创建template模板,主要包含CSS样式
  • 在script里创建组件类Class
  • 在constructor里创建:继承super()和影子DOM节点
  • 在constructor里获取组件的模板,拷贝组件模板中的内容
  • 在constructor里设置template模板中的属性,用的是自定义组件中的自定义的属性
  • 在constructor里的影子DOM节点中插入组件模板
  • 使用浏览器原生的window.customElements.define()方法,告诉浏览器元素与这个类关联。
/*
*阮大神的代码借鉴
*/
<body>
    
    <user-card image="https://semantic-ui.com/images/avatar2/large/kristy.png"
 	name="User Name"
 	email="[email protected]">user-card>
    <br>
    
    <user-card image="https://semantic-ui.com/images/avatar2/large/kristy.png"
  	name="User Name"
  	email="[email protected]">user-card>
    
     
    <template id="userCardTemplate">
         
        <img class="image">
        <div class="container">
            <p class="name">p>
            <p class="email">p>
            <button class="button">Follow Johnbutton>
        div>
    	
        <style>
            :host {
                display: flex;
                align-items: center;
                width: 450px;
                height: 180px;
                background-color: #d4d4d4;
                border: 1px solid #d5d5d5;
                box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
                border-radius: 3px;
                overflow: hidden;
                padding: 10px;
                box-sizing: border-box;
                font-family: 'Poppins', sans-serif;
            }

            .image {
                flex: 0 0 auto;
                width: 160px;
                height: 160px;
                vertical-align: middle;
                border-radius: 5px;
            }

            .container {
                box-sizing: border-box;
                padding: 20px;
                height: 160px;
            }

            .container>.name {
                font-size: 20px;
                font-weight: 600;
                line-height: 1;
                margin: 0;
                margin-bottom: 5px;
            }

            .container>.email {
                font-size: 12px;
                opacity: 0.75;
                line-height: 1;
                margin: 0;
                margin-bottom: 15px;
            }

            .container>.button {
                padding: 10px 25px;
                font-size: 12px;
                border-radius: 5px;
                text-transform: uppercase;
            }
        style>
    template>
body>

<script>
     class UserCard extends HTMLElement {
        constructor() {
            super();
            //创建影子DOM节点,this指向UserCard
            //this.attachShadow()方法的参数{ mode: 'closed'},表示 Shadow DOM 是封闭的,不允许外部访问。
            var shadow = this.attachShadow({ mode: 'closed' });//此时shadow相当于影化后的UserCard,可以这么理解
            //获取组件模板
            var templateElem = document.getElementById('userCardTemplate');
            //拷贝组件模板中的内容,主要是一些css样式
            var content = templateElem.content.cloneNode(true);
            //对template模板进行DOM操作,this指向UserCard
            content.querySelector('img').setAttribute('src', this.getAttribute('image'));
            content.querySelector('.container>.name').innerText = this.getAttribute('name');
            content.querySelector('.container>.email').innerText = this.getAttribute('email');
            shadow.appendChild(content);//此时shadow相当于影化后的UserCard,也就是说appendChild后,就可以用到content来修改UserCard的样式
        }
    }
    //使用浏览器原生的customElements.define()方法,告诉浏览器元素与这个类关联。
    window.customElements.define('user-card', UserCard);
script>

四、如何给组件传值

<body>
     
    <hello-world message="好好学习">hello-world>
    <br>
    <hello-world message="天天向上">hello-world>
    <template id="hw-template">
             <p>Hello Worldp>
        <style>
            p{
                padding:10px;
                background-color:#f40;
                color:#fff 
            }
        style>
    template>
body>

<script>
    class HelloWorld extends HTMLElement{
        constuctor(){
            super();
            const templateContent = document.querySelector('#hw-template').content
            const shadowRoot = this.attachShadow({mode:'open'})
            shadowRoot.appendChild(templateContent.cloneNode(true))
   			//获取属性
            const message = this.getAttribute('message')
            shadowRoot.querySelector('p').innerText = message
        }
    }
	customElement.define('hello-world')
script>

五、生命周期

<body>
    <hello-world message="好好学习">hello-world>
    <br>
    <hello-world message="天天向上">hello-world>
    <template id="hw-template">
             <p>Hello Worldp>
        <style>
            p{
                padding:10px;
                background-color:#f40;
                color:#fff 
            }
        style>
    template>
body>

<script>
    class HelloWorld extends HTMLElement{
        constuctor(){
            super();
            const templateContent = document.querySelector('#hw-template').content
            const shadowRoot = this.attachShadow({mode:'open'})
            shadowRoot.appendChild(templateContent.cloneNode(true))
            const message = this.getAttribute('message')
            shadowRoot.querySelector('p').innerText = message            
        }
        //首次插入DOM文档时调用
        connectedCallback(){
            //在这里发送数据请求(Ajax)
        }
        //被从文档DOM中删除时调用
        disconnectedCallback(){
            
        }
        //被移动到新的文档时调用
        adoptedCallback(){
            
        }
        //当增加、删除、修改自身的属性时被调用
        attributeChangedCallback(){
            
        }
    }
	customElement.define('hello-world')
script>

六、组件插槽

<body>
    <hello-world>
        <p slot="header">我是通过插槽进入的组件头部p>
    	<p slot="content">我是通过插槽进入的组件内容p>
    hello-world>
    <template id="hw-template">
   		 <div>
       		<div class="card">
           		<div class="card-header">
                
           		<slot name="content">slot>
                div>
           		<div class="card-content">
                
           		<slot name="content">slot>
                div>
       	   div>
       	  div>
        <style>
           .card{
                width:500px;
                height:300px;
                border:1px soild #000;
                padding:5px;
            }
            .card-header{
                padding:10px;
                background-color:#ccc
            }
        style>
    template>
body>

<script>
    class HelloWorld extends HTMLElement{
        constuctor(){
            super();
            const templateContent = document.querySelector('#hw-template').content
            const shadowRoot = this.attachShadow({mode:'open'})
            shadowRoot.appendChild(templateContent.cloneNode(true))         
        }
        connectedCallback(){

        }
        disconnectedCallback(){
            
        }
        adoptedCallback(){
            
        }
        attributeChangedCallback(){
            
        }
    }
	customElement.define('hello-world')
script>

你可能感兴趣的:(Web,Component)