美文网首页
Web Components 系列(十一)—— 实现 MyCar

Web Components 系列(十一)—— 实现 MyCar

作者: 隐逸王 | 来源:发表于2022-02-18 22:13 被阅读0次
    MyCard可复用.001

    前言

    在上一节中,使用 Templates 实现了 MyCard 的基本布局,并且在文章结尾我也说过,因为不可复用,其实用性基本为零。

    今天我们通过使用具名 Slots 在 Templates 中占位,然后再在自定义元素中给 Slots 传值,提高自定义元素的灵活性。

    传值分析

    因为每一个人的各项信息都不尽相同,而对应到 Templates 中,就是所有 className 为 .info-content 的 div 中的内容都是可变的,所有可变值总结一下就是:

    • userName
    • gender
    • nation
    • birthYear
    • birthMonth
    • birthDay
    • address
    • cardNO

    就是说,针对每一张 Card,以上这些属性值都需要在自定义组件中传递。

    使用 HTML 标签自定义属性

    要给自定义组件,除了 Slots,也可以借助 HTML 标签的自定义属性。

    第一步:我们给 Templates 内部的可变项父标签添加 id 标识,比如:

    <div class="info-content" id="user_name">编程三昧</div>
    

    第二步:在自定义组件内部获取它本身的用户自定义属性:

    class MyCard extends HTMLElement {
        constructor () {
            super();
            this.shadow = this.attachShadow({mode: "open"});
            let tempEle = document.getElementById("card_layout");
            this.shadow.appendChild(tempEle.content);
            // 获取并填充姓名
            let userName = this.getAttribute("userName") || "编程三昧";
            this.shadow.querySelector("#user_name").textContent = userName;
            // 剩余可变项的获取和设置是一样的流程
        }
    }
    

    第三步:在自定义元素的标签上添加对应的自定义属性:

    <my-card userName="隐逸王"></my-card>
    

    通过以上步骤,也是可以实现自定义组件传值的效果的,从而达到组件复用的目的。

    使用具名 Slots 传值

    虽然上面使用 HTML 标签的自定义属性达到了传值的目的,但是 JS 部分的代码看起来不太美观,下面我们就用 Slots 传值的方式实现一版。

    第一步:给 Templates 增加具名插槽进行占位。

    <template id="card_layout">
        <style>
            * {
                box-sizing: border-box;
            }
    
            :host {
                display: inline-block;
                width: 400px;
                height: 240px;
                border: 1px solid black;
                border-radius: 10px;
                box-shadow: -2px -2px 5px 0px #7a8489;
            }
    
            .container {
                display: flex;
                flex-direction: column;
                padding: 10px;
                height: 100%;
            }
    
            .card-body {
                flex: 1;
                display: flex;
            }
    
            .card-footer {
                padding: 10px 0;
            }
    
            .main-info {
                flex: 2;
            }
    
            .photo {
                flex: 1;
                display: flex;
                align-items: center;
            }
    
            .photo img {
                width: 100%;
            }
    
            .info-row {
                display: flex;
                padding-top: 15px;
            }
    
            .info-column {
                display: flex;
                align-items: center;
            }
    
            .info-title {
                padding: 0 10px;
                color: #0e5bd3;
                font-size: 12px;
                word-break: keep-all;
            }
    
            .info-content {
                letter-spacing: 2px;
            }
        </style>
        <div class="container">
            <div class="card-body">
                <div class="main-info">
                    <div class="info-row">
                        <div class="info-column">
                            <div class="info-title">姓名</div>
                        </div>
                        <div class="info-content">
                            <slot name="userName">隐逸王</slot>
                        </div>
                    </div>
                    <div class="info-row">
                        <div class="info-column">
                            <div class="info-title">性别</div>
                            <div class="info-content">
                                <slot name="gender">男</slot>
                            </div>
                        </div>
                        <div class="info-column">
                            <div class="info-title">民族</div>
                            <div class="info-content">
                                <slot name="nation">汉</slot>
                            </div>
                        </div>
                    </div>
                    <div class="info-row">
                        <div class="info-column">
                            <div class="info-title">出生</div>
                            <div class="info-content">
                                <slot name="birthYear">2022</slot>
                            </div>
                        </div>
                        <div class="info-column">
                            <div class="info-title">年</div>
                            <div class="info-content">
                                <slot name="birthMonth">12</slot>
                            </div>
                        </div>
                        <div class="info-column">
                            <div class="info-title">月</div>
                            <div class="info-content">
                                <slot name="birthDay"></slot>
                            </div>
                        </div>
                        <div class="info-column">
                            <div class="info-title">日</div>
                        </div>
                    </div>
                    <div class="info-row">
                        <div class="info-column">
                            <div class="info-title">住址</div>
                        </div>
                        <div class="info-content">
                            <slot name="address">xx省xx市xx区xx街道xx小区xx楼xx单元xx楼xx室</slot>
                        </div>
                    </div>
                </div>
                <div class="photo">
                    <img src="./static/photo.jpg">
                </div>
            </div>
            <div class="card-footer">
                <div class="info-row">
                    <div class="info-column">
                        <div class="info-title">公民身份号码</div>
                    </div>
                    <div class="info-content">
                        <slot name="cardNO">12345678901234567X</slot>
                    </div>
                </div>
            </div>
        </div>
    </template>
    

    第二步:在自定义元素标签内插入带有 slot=‘’ 属性的标签及内容。

    <my-card>
        <span slot="userName">编程三昧</span>
        <span slot="gender">男</span>
        <span slot="nation">汉</span>
        <span slot="birthYear">2002</span>
        <span slot="birthMonth">2</span>
        <span slot="birthDay">2</span>
        <span slot="address">银河系太阳系地球村亚洲中国美丽小区</span>
        <span slot="cardNO">134098567432129485-ZH</span>
    </my-card>
    

    最终实现的效果如下:

    image-20220218203827480

    实现一个网页显示多张 MyCard

    如果想要同时展示多个卡片到同一页面,你使用上面代码的话会发现:只有第一个有内容,其余的都为空。这是因为第一个 MyCard 实例将 Templates 的内容都追加在了自己内部,其余的实例获取到的 tempEle.content 都为空节点。

    想要解决这个问题,就需要在 MyCard 构造函数内部对 Templates 内容进行克隆,而不是直接使用:

    class MyCard extends HTMLElement {
        constructor () {
            super();
            this.shadow = this.attachShadow({mode: "open"});
            let tempEle = document.getElementById("card_layout");
            this.shadow.appendChild(document.importNode(tempEle.content,true));
        }
    }
    customElements.define("my-card", MyCard);
    

    总结

    本文使用了两种方式向自定义组件传值:

    • HTML 标签的自定义属性传值
    • 具名 Slots 传值

    两种都可以使用,看情况及个人喜好而定吧。

    另外,还有一个细节需要注意:appendChild() 方法会将传入的节点整个的移动位置,传入的那个 Node 在 DOM 中的位置会发生变化,我们一般在调用 appendChild() 时,传入的都是克隆节点。

    ~

    ~ 本文完,感谢阅读!

    ~

    学习有趣的知识,结识有趣的朋友,塑造有趣的灵魂!

    大家好,我是〖编程三昧〗的作者 隐逸王,我的公众号是『编程三昧』,欢迎关注,希望大家多多指教!

    相关文章

      网友评论

          本文标题:Web Components 系列(十一)—— 实现 MyCar

          本文链接:https://www.haomeiwen.com/subject/pkculrtx.html