原生html如何实现自定义组件?

最近在思考如果脱离框架如何实现自定义组件这个问题,经过一番研究之后发现web API提供了web component来给用户实现自定义组件,就拿Antd的Button组件为例,废话少说直接上代码:

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
  </head>
  <body>
    <script type="module" src="./index.js"></script>
    <button-wrapper size="large">按钮</button-wrapper>
    <div style="margin-top: 30px;">
      <button>small</button>
      <button>middle</button>
      <button>large</button>
    </div>
    <script type="text/javascript">
      document.querySelectorAll('button').forEach(item=>{
        item.addEventListener('click',()=>{
          document.querySelector('button-wrapper').setAttribute('size',item.innerText);
        })
      })
    </script>
  </body>
</html>

index.js

import Button from "./button.js";
//通过customElements.define实现自定义元素,但是为了和html元素区分,自定义元素的命名只只支持小写字母并且带“—”中横线
customElements.define('button-wrapper', Button);

button.js

在web component中也提供相应的生命周期函数可以监听到元素被挂载或者移除等动作,并且通过static get observedAttributes方法返回需要监听的自定义属性数组当属性值发生改变时就会触发attributeChangedCallback这个生命周期函数

export default class Button extends HTMLElement {
    constructor() {
        super()
        // 创建 shadow root
        var shadow = this.attachShadow({ mode: 'open' });
        // 创建 button
        var wrapper = document.createElement('button');
        wrapper.setAttribute('class', 'large');
        wrapper.innerHTML = this.innerHTML;
        this.textContent = "";
        var style = document.createElement('style');
        // 将所创建的元素添加到 Shadow DOM 上
        shadow.appendChild(style);
        shadow.appendChild(wrapper);
    }
    updateStyle(elem) {
        var shadow = elem.shadowRoot;
        shadow.querySelector("style").textContent = `
          .large{
            font-size: 16px;
            height: 40px;
            padding: 6.428571428571429px 15px;
            border-radius: 8px;
            color: #fff;
            background-color: #1677ff;
            box-shadow: 0 2px 0 rgba(5,145,255,.1);
            outline: none;
            display: inline-block;
            font-weight: 400;
            white-space: nowrap;
            text-align: center;
            background-image: none;
            border: 1px solid transparent;
            cursor: pointer;
            transition: all .2s cubic-bezier(.645,.045,.355,1);
            user-select: none;
            touch-action: manipulation;
            line-height: 1.5714285714285714;
            position: relative;
            ${elem.getAttribute('style')}
          }
          .middle{
            font-size: 14px;
            height: 32px;
            padding: 4px 15px;
            border-radius: 8px;
            color: #fff;
            background-color: #1677ff;
            box-shadow: 0 2px 0 rgba(5,145,255,.1);
            outline: none;
            display: inline-block;
            font-weight: 400;
            white-space: nowrap;
            text-align: center;
            background-image: none;
            border: 1px solid transparent;
            cursor: pointer;
            transition: all .2s cubic-bezier(.645,.045,.355,1);
            user-select: none;
            touch-action: manipulation;
            line-height: 1.5714285714285714;
            position: relative;
            ${elem.getAttribute('style')}
          }
          .small{
            font-size: 14px;
            height: 24px;
            padding: 0px 7px;
            border-radius: 8px;
            color: #fff;
            background-color: #1677ff;
            box-shadow: 0 2px 0 rgba(5,145,255,.1);
            outline: none;
            display: inline-block;
            font-weight: 400;
            white-space: nowrap;
            text-align: center;
            background-image: none;
            border: 1px solid transparent;
            cursor: pointer;
            transition: all .2s cubic-bezier(.645,.045,.355,1);
            user-select: none;
            touch-action: manipulation;
            line-height: 1.5714285714285714;
            position: relative;
            ${elem.getAttribute('style')}
          }
        `;
    }

    updateClass(elem,newValue){
        var shadow = elem.shadowRoot;
        shadow.querySelector('button').setAttribute('class',newValue)
    }
    static get observedAttributes() { return ['size', 'style']; }
    connectedCallback() {
        console.log('自定义组件被添加到页面中.');
        this.updateStyle(this);
    }

    disconnectedCallback() {
        console.log('自定义元素被从页面中移除');
    }

    adoptedCallback() {
        console.log('自定义元素被移动其他页面中');
    }

    attributeChangedCallback(name, oldValue, newValue) {
        console.log('自定义组件属性发生改变');
        if(name=='size'){
            this.updateClass(this,newValue)
        }else{
            this.updateStyle(this);
        }
    }
}

原生自定义组件的好处是自身就像是一个暗箱,隔离了自身和外界的style不会存在样式冲突的问题,只能通过自己实现改变元素的样式。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值