组件-原生组件webcomponent

红叶何时落水

组件化开发有利于代码的复用

这里是原生组件的思想与构建

首先,代码复用,就意味着会有一个模板,这个模板已经实现了基础功能,我们只需要调用它即可;那么一个组件由两部分构成,视图与逻辑。

视图模板

视图的模板原生提供的方法为<template>标签,里面的内容可以被其他页面使用;

    <template id="new_message">
        <style>
            .main {
                width: 300px;
                height: 100px;
                background-color: aqua;
                display: flex;
                flex-direction: column;
                position: absolute;
                top: 40%;
                left: 40%;
            }

            .title {
                width: 100%;
                height: 30px;
                text-align: center;
                line-height: 30px;
            }

            input {
                width: 80%;
                height: 20px;
                margin-left: 10%;
            }

            .button {
                width: 100%;
                height: 50px;
                display: flex;
                flex-direction: row;
                justify-content: space-around;
            }

            button {
                margin-top: 10px;
                align-items: center;
                justify-items: center;
                height: 60%;
                width: 30%;
            }
        </style>
        <div class="main">
            <slot name = 'title' class="title">old_title</slot>
            <input type="text">
            <div class="button">
                <button id="Yes" >确定</button>
                <button id="No">放弃</button>
            </div>
            
        </div>
    </template>

注意两个点

1.<template>需要一个id,方便逻辑层来获取他;

2.<slot>插槽的使用

        上面的模板有一个插槽<slot name = 'title' class="title">old_title</slot>

        这个插槽代表一个坑位,如果我们在使用组件的时候不向这个坑位添加内容时,他会显示默认内容;

然后,就是逻辑的模板

逻辑模板是一段可复用的代码,并且多次调用,不会相互干扰。

那么符合以上条件的是什么? 最简单的例子,函数。

我们定义一个函数,并且使函数内部的变量都使局部变量。那么,我们不管什么时候调用他,都会有一套全新的变量;

那么我们的逻辑模板,同样是一个函数;先来学会构建简单的模板--类--。

function Dad (name) {
        this.name = name;
        // console.log(this.name);
    }

    Dad.prototype.Dad_method1 = function () {
        console.log(this.name)
    }

    function son (name) {
        Dad.call(this, name);// 调用Dad(),同时将Dad的方法映射到son绑定的this对象,并且将Dad的this指向从¥---->son的this指向地址(对象a的地址)
        // this.Dad_method1();// 这里等同于外面的a.Dad_method1();
    }

    son.prototype = new Dad('Init');// 这里Dad实例化,this指向实例化Dad的地址¥。
    son.prototype.constructor = son;

    let a = new son('Init2')
    // a.Dad_method1();



    class Dad {
        constructor (name) {
            this.name = name;
        }

        Dad_method1 () {
            console.log(this.name)
        }
    }

    class son extends Dad {
        constructor (name) {
            super(name);// 等同于 Dad.call(this, name);// 调用Dad(),同时将Dad的方法映射到son绑定的this对象
            this.Dad_method1();
        }
    }

    let a = new son ('Init2');
    // a.Dad_method1();

只提几个关键概念

1.this指向的是第一个调用函数的对象的地址。

2.call()调用一下函数,并且把被调用函数的this指向修改为我们第一个参数传入的地址;

3.super() 作用等同于 fn().call(this);

4.ES6之前我们需要手动设置constructor指向

son.prototype.constructor = son;

5.son继承Dad的方法,使之变成自己的方法,思想感觉有点像vue的代理

    /** 将 某一个对象的属性 访问 映射到 对象的某一个属性成员上 */
    function proxy( target, prop, key ) {
      Object.defineProperty( target, key, {
        enumerable: true,
        configurable: true,
        get () {
          return target[ prop ][ key ];
        },
        set ( newVal ) {
          target[ prop ][ key ] = newVal;
        }
      } );
    }

明白的类的使用之后,我们就可以构建组件的逻辑模板了。

class newElement extends HTMLElement {
    constructor() {
        super();// 不需要向HTMLElement的构造函数传参,只需要改变this指向,让我们可以使用HTMLElement的方法与属性
        this.Init();
    }

    Init() {
        const shadowRoot = this.attachShadow({ mode: 'open' });// 建立一个ShadowDom用于存放我们的组件,与非组件分开
        var temp_ele = document.getElementById('new_message');
        var content = temp_ele.content.cloneNode(true);// 去除template这个外壳

        shadowRoot.appendChild(content);

        // 绑定事件
        shadowRoot.querySelector('#Yes')
        .addEventListener('click', (e) => {
            console.log('Yes');
        })

        shadowRoot.querySelector('#No')
        .addEventListener('click', (e) => {
            console.log('No');
        })

        this.attrs = this.getAttribute("attrs");
    }
}

window.customElements.define('new-ele-name', newElement);

再说关键几点

我们需要继承HTMLElement的某些方法,以便我们可以使用一些基本的方法。

class newElement extends HTMLElement {
    constructor() {
        super();// 不需要向HTMLElement的构造函数传参,只需要改变this指向,让我们可以使用HTMLElement的方法与属性
        this.Init();
    }

之后,我们建立一个ShadowDom与页面的其他元素分开,以保证组件的独立,以及不受到公共样式的干扰。


    Init() {
        const shadowRoot = this.attachShadow({ mode: 'open' });// 建立一个ShadowDom用于存放我们的组件,与非组件分开

我们获取模板内容,将其添加到ShadowDom上 ;

        var temp_ele = document.getElementById('new_message');
        var content = temp_ele.content.cloneNode(true);// 去除template这个外壳

        shadowRoot.appendChild(content);

然后,对页面添加一些交互事件

        shadowRoot.querySelector('#Yes')
        .addEventListener('click', (e) => {
            console.log('Yes');
        })

        shadowRoot.querySelector('#No')
        .addEventListener('click', (e) => {
            console.log('No');
        })

我们可以利用属性作为对外开放的接口,以此实现通信

设置属性attrs

    <new-ele-name attrs = '2'>
        <text slot="title">new_title</text>
    </new-ele-name>

获取属性

 this.attrs = this.getAttribute("attrs");
    }
}

关于插槽,有一个点,插槽与ShadowDom同级,ShadowDom里的插槽指向外面的插槽。自己试一下就明白了。

最后,注册组件(组件名字必须有"-"连接)

window.customElements.define('new-ele-name', newElement);

使用组件,再body里写入;

    <new-ele-name attrs = '2'>
        <text slot="title">new_title</text>
    </new-ele-name>

完整代码

<html>

<body>
    <new-ele-name attrs = '2'>
        <text slot="title">new_title</text>
    </new-ele-name>
    <template id="new_message">
        <style>
            .main {
                width: 300px;
                height: 100px;
                background-color: aqua;
                display: flex;
                flex-direction: column;
                position: absolute;
                top: 40%;
                left: 40%;
            }

            .title {
                width: 100%;
                height: 30px;
                text-align: center;
                line-height: 30px;
            }

            input {
                width: 80%;
                height: 20px;
                margin-left: 10%;
            }

            .button {
                width: 100%;
                height: 50px;
                display: flex;
                flex-direction: row;
                justify-content: space-around;
            }

            button {
                margin-top: 10px;
                align-items: center;
                justify-items: center;
                height: 60%;
                width: 30%;
            }
        </style>
        <div class="main">
            <slot name = 'title' class="title">old_title</slot>
            <input type="text">
            <div class="button">
                <button id="Yes" >确定</button>
                <button id="No">放弃</button>
            </div>
            
        </div>
    </template>
    <!-- <button onclick="a.open()">启动组件</button> -->
</body>
<!-- <script src="../前端笔记/组件js.js"></script> -->
<script>
    

class newElement extends HTMLElement {
    constructor() {
        super();// 不需要向HTMLElement的构造函数传参,只需要改变this指向,让我们可以使用HTMLElement的方法与属性
        this.Init();
    }

    Init() {
        const shadowRoot = this.attachShadow({ mode: 'open' });// 建立一个ShadowDom用于存放我们的组件,与非组件分开
        var temp_ele = document.getElementById('new_message');
        var content = temp_ele.content.cloneNode(true);// 去除template这个外壳

        shadowRoot.appendChild(content);

        // 绑定事件
        shadowRoot.querySelector('#Yes')
        .addEventListener('click', (e) => {
            console.log('Yes');
        })

        shadowRoot.querySelector('#No')
        .addEventListener('click', (e) => {
            console.log('No');
        })

        this.attrs = this.getAttribute("attrs");
    }
}

window.customElements.define('new-ele-name', newElement);


   


    // var a = new use_message();
</script>

</html>

当然,我们可以向高阶函数那样,加个高阶类,来修饰一下基础组件

这里使用第二种调用组件的方法

如果理解了类,就可以看懂

<html>

<body>
    <!-- <new-ele-name attrs = '2'>
        <text slot="title">new_title</text>
    </new-ele-name> -->
    <template id="new_message">
        <style>
            .main {
                width: 300px;
                height: 100px;
                background-color: aqua;
                display: flex;
                flex-direction: column;
                position: absolute;
                top: 40%;
                left: 40%;
            }

            .title {
                width: 100%;
                height: 30px;
                text-align: center;
                line-height: 30px;
            }

            input {
                width: 80%;
                height: 20px;
                margin-left: 10%;
            }

            .button {
                width: 100%;
                height: 50px;
                display: flex;
                flex-direction: row;
                justify-content: space-around;
            }

            button {
                margin-top: 10px;
                align-items: center;
                justify-items: center;
                height: 60%;
                width: 30%;
            }
        </style>
        <div class="main">
            <slot name='title' class="title">old_title</slot>
            <input type="text">
            <div class="button">
                <button id="Yes">确定</button>
                <button id="No">放弃</button>
            </div>

        </div>
    </template>
    <button onclick="a.open()">启动组件</button>
</body>
<!-- <script src="../前端笔记/组件js.js"></script> -->
<script>


    class newElement extends HTMLElement {
        constructor() {
            super();// 不需要向HTMLElement的构造函数传参,只需要改变this指向,让我们可以使用HTMLElement的方法与属性
            this.Init();
        }

        Init() {
            const shadowRoot = this.attachShadow({ mode: 'open' });// 建立一个ShadowDom用于存放我们的组件,与非组件分开
            var temp_ele = document.getElementById('new_message');
            var content = temp_ele.content.cloneNode(true);// 去除template这个外壳

            shadowRoot.appendChild(content);

            // 绑定事件
            shadowRoot.querySelector('#Yes')
                .addEventListener('click', (e) => {
                    console.log('Yes');
                })

            shadowRoot.querySelector('#No')
                .addEventListener('click', (e) => {
                    console.log('No');
                })

            this.attrs = this.getAttribute("attrs");
        }
    }

    window.customElements.define('new-ele-name', newElement);


    class use_message {
        constructor() {
            this.el = document.createElement('new-ele-name');
            this.Init();
        }

        Init() {
            this.el.shadowRoot.querySelector('#Yes')
                .addEventListener('click', (e) => {
                    this.close();
                })

            this.el.shadowRoot.querySelector('#No')
                .addEventListener('click', (e) => {
                    this.close();
                })
        }

        open() {
            document.body.appendChild(this.el);
        }

        close() {
            document.body.removeChild(this.el);
        }
    }

    var a = new use_message();
</script>

</html>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

红叶落水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值