首页 > 其他分享 >未来组件化开发趋势WebComponent

未来组件化开发趋势WebComponent

时间:2024-07-18 23:54:32浏览次数:13  
标签:style collapse WebComponent 开发 let 组件 shadow name

未来组件化开发趋势WebComponent

优点:原生组件,不需要框架,性能好代码少。
缺点:兼容性问题

组件化好处: 高内聚、可重用、可组合

https://developer.mozilla.org/zh-CN/docs/Web/API/Web_components

核心三项技术

  • Custom elements:一组JavaScript API,允许您定义custom elements及其行为,然后可以在您的用户界面中按照需要使用它们
  • Shadow DOM:一组JavaScript API,用于将封装的“影子”DOM树附加到元素(与主文档DOM分开呈现)并控制其关联的功能。通过这种方式,您可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。
  • HTML templates<template><slot> 元素使您可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。

WebComponent生命周期

  • connectedCallback:当custom element首次被插入文档DOM时,被调用
  • disconnectedCallback:当 custom element从文档DOM中删除时,被调用
  • adoptedCallback:当 custom element被移动到新的文档时,被调用 (移动到iframe中)
  • attributeChangedCallback:当 custom element增加、删除、修改自身属性时,被调用

案例组件实现

shadowDOM完全隔离

组件间通信,通过dispatchEvent派发自定义监听事件

customEvent -> webcomponent 兼容性差,没有自动更新机制

实现自定义Button组件

index.html
  • 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>
        <style>
            :root{
                --background-color: black;
                --text-color:yellow
            }
        </style>
        <hs-button type="primary">按钮</hs-button>
        <hs-button>按钮</hs-button>
        <!-- 内容是不会被渲染到视图上,不会影响页面展示,可以使用模板 -->
        <template id="btn">
            <button class="hs-button">
                <slot></slot>
            </button>
        </template>
    
        <script>
            class HSButton extends HTMLElement {
                constructor() {
                    super();
                    let shadow = this.attachShadow({ mode: 'open' });
                    let btnTmpl = document.getElementById('btn');
                    let cloneTemplate = btnTmpl.content.cloneNode(true)
                    const style = document.createElement('style');
                    let type = this.getAttribute('type') || 'default';
                    const btnList = {
                        'primary': {
                            background: '#409eff',
                            color: '#fff'
                        },
                        'default': {
                            background: '#909399',
                            color: '#fff'
                        }
                    }
                    style.textContent = `
                        .hs-button{
                            outline:none;
                            border:none;
                            border-radius:4px;
                            padding:5px 20px;
                            display:inline-flex;
                            background:var(--background-color,${btnList[type].background});
                            color:var(--text-color,${btnList[type].color});
                            cursor:pointer
                        }
                    `
                    // dom操作具备移动型
                    shadow.appendChild(style)
                    shadow.appendChild(cloneTemplate)
                }
            }
            // 定义了一个自定义标签 组件
            window.customElements.define('hs-button', HSButton)
        </script>
    </body>
    
    </html>
    

    template中的内容是我们定义的button组件的样子。slot可以获取自定义组件中的内容,插入到模板对应的位置

shadowDOM
shadow DOM 可以实现真正的隔离机制

img

实现自定义Collapse 折叠面板组件

collapse.html
  • collapse.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>
        <hs-collapse >
            <hs-collapse-item title="Node" name="1">
                <div>nodejs welcome</div>
            </hs-collapse-item>
            <hs-collapse-item title="react" name="2">
                <div>react welcome</div>
            </hs-collapse-item>
            <hs-collapse-item title="vue" name="3">
                <div>vue welcome</div>
            </hs-collapse-item>
        </hs-collapse>
        
        <!-- 没有实际意义, 不会渲染到页面上 -->
        <template id="collapse_tmpl">
            <div class="hs-collapse">
                <slot></slot>
            </div>
        </template>
        <template id="collapse_item_tmpl">
            <div class="hs-collapse-item">
                <div class="title"></div>
                <div class="content">
                    <slot></slot>
                </div>
            </div>
        </template>
        <!-- vite 实现原理 就依赖于 type="module" -->
        <script src="./index.js" type="module"></script>
    </body>
    </html>
    
collapse-item.js
  • collapse-item.js

    class CollapseItem extends HTMLElement {
        constructor() {
            super();
            let shadow = this.attachShadow({ mode: 'open' });
            let tmpl = document.getElementById('collapse_item_tmpl');
            let cloneTemplate = tmpl.content.cloneNode(true);
            let style = document.createElement('style');
            this.isShow = true; // 标识自己是否需要显示
    
            style.textContent = `
                :host{
                    width:100%;
                }
                .title{
                    background:#f1f1f1;
                    line-height:35px;
                    height:35px;
                }
                .content{
                    font-size:14px;
                }
            `
    
            shadow.appendChild(style)
            shadow.appendChild(cloneTemplate);
            this.titleEle = shadow.querySelector('.title');
    
            this.titleEle.addEventListener('click',()=>{
                // 如果将结果传递给父亲  组件通信? 派发一个事件
                document.querySelector('hs-collapse').dispatchEvent(new CustomEvent('changeName',{
                    detail:{
                        name:this.getAttribute('name'),
                        isShow:this.isShow
                    }
                }))
            })
        }
    
        static get observedAttributes() { // 监控属性的变化
            return ['active', 'title', 'name']
        }
        // update
        attributeChangedCallback(key, oldVal, newVal) {
            switch (key) {
                case 'active':
                    this.activeList = JSON.parse(newVal); // 子组件接受父组件的数据
                    break;
                case 'title':
                    this.titleEle.innerHTML = newVal; // 接受到title属性 作为dom的title
                    break;
                case 'name':
                    this.name = newVal
                    break;
            }
            let name = this.name;
            if (this.activeList && name) {
                this.isShow = this.activeList.includes(name);
                this.shadowRoot.querySelector('.content').style.display =  this.isShow ? 'block' : 'none'
            }
        }
    }
    export default CollapseItem
    
collapse.js
  • collapse.js

    class Collapse extends HTMLElement {
        constructor() {
            super();
            const shadow = this.attachShadow({ mode: 'open' });
            const tmpl = document.getElementById('collapse_tmpl');
            let cloneTemplate = tmpl.content.cloneNode(true);
            let style = document.createElement('style');
            // :host 代表的是影子的根元素
            style.textContent = `
                :host{
                    display:flex;
                    border:3px solid #ebebeb;
                    border-radius:5px;
                    width:100%;
                }
                .hs-collapse{
                    width:100%;
                }
            `
            shadow.appendChild(style);
            shadow.appendChild(cloneTemplate);
            // 从影子中拿到插槽
            let slot = shadow.querySelector('slot'); // 监控slot变化  
            // 监听插槽的变化
            slot.addEventListener('slotchange', (e) => {
                this.slotList = e.target.assignedElements();
                this.render();
            })
        }
        static get observedAttributes() { // 监控属性的变化
            return ['active']
        }
        // update 属性变化时执行
        attributeChangedCallback(key, oldVal, newVal) {
            if (key == 'active') {
                this.activeList = JSON.parse(newVal);
                this.render();
            }
        }
        render() {
          // 获取插槽里的元素
            if (this.slotList && this.activeList) {
                [...this.slotList].forEach(child => {
                    child.setAttribute('active', JSON.stringify(this.activeList))
                });
            }
        }
        // connectedCallback(){
        //     console.log('插入到dom时执行的回调')
        // }
        // disconnectedCallback(){
        //     console.log('移除到dom时执行的回调')
        // }
        // adoptedCallback(){
        //     console.log('将组件移动到iframe 会执行')
        // }
    
    }
    export default Collapse
    
index.js
  • index.js

    import Collapse from './collapse.js';
    import CollapseItem from './collapse-item.js';
    
    
    window.customElements.define('hs-collapse',Collapse);
    window.customElements.define('hs-collapse-item',CollapseItem);
    
    
    // 设置组件默认显示的状态 
    
    let defaultActive = ['1','2']; // name:1 name:2 默认展开 3 应该隐藏
    // 拿不到影子里面的东西(也就是template里的)
    document.querySelector('hs-collapse').setAttribute('active',JSON.stringify(defaultActive));
    
    // 每个item需要获取到defaultActive 和自己的name属性比较,如果在里面就显示,不在里面就隐藏
    document.querySelector('hs-collapse').addEventListener('changeName',(e)=>{
        let {isShow,name} = e.detail;
        if(isShow){
            let index = defaultActive.indexOf(name);
            defaultActive.splice(index,1);
        }else{
            defaultActive.push(name);
        }
        document.querySelector('hs-collapse').setAttribute('active',JSON.stringify(defaultActive));
    });
    
    // shadowDOM 完全隔离
    // 组件间的通信 属性,事件
    // customEvent  -> webcomponent 兼容性差,没有自动更新机制
    

标签:style,collapse,WebComponent,开发,let,组件,shadow,name
From: https://blog.csdn.net/qq_40588441/article/details/140535907

相关文章

  • vue3 使用component is 动态组件
    使用方式父组件<template><divclass="box"><div><!--setup需要用变量的方式来写入is,如果是optionsapi方式可以用组件字符--><!--myProps属性可以直接传到动态组件--><component:is="childT"myProps="sldfjsklfjksf......
  • 网站开发:使用VScode安装yarn包和运行前端项目
    一、首先打开PowerShell-管理员身份运行ISE输入命令:set-ExecutionPolicyRemoteSigned选择“全是”,表示允许在本地计算机上运行由本地用户创建的脚本,没有报错就行了二、接着打开VScode集成终端输入npminstall-gyarn再次输入以下命令,无报错说明yarn安装成功ya......
  • C#开发:PowerDesigner建表和Navicat导入数据
    一、打开Powerdesigner,新建一个模型,点击ok二、用工具面板拖拽出一个数据表 (如果没有工具面板,请在如下操作中开启) 三、双击刚刚的拖拽出来的表,设计表的字段,可以添加注释说明 【备注】PFM:主键、外键、不可为空四、自动生成sql,然后去执行一遍这个建表语法主键自......
  • 微信小程序毕业设计-宠物寄养平台系统项目开发实战(附源码+论文)
    大家好!我是程序猿老A,感谢您阅读本文,欢迎一键三连哦。......
  • 微信小程序毕业设计-同城家政服务系统项目开发实战(附源码+论文)
    大家好!我是程序猿老A,感谢您阅读本文,欢迎一键三连哦。......
  • 微信小程序毕业设计-国产动漫论坛系统项目开发实战(附源码+论文)
    大家好!我是程序猿老A,感谢您阅读本文,欢迎一键三连哦。......
  • Vue3动态生成组件
    在Vue3中,要遍历funConfig并动态生成组件,可以使用Vue的defineAsyncComponent来加载异步组件,并结合v-for指令在模板中进行渲染。以下是一个示例代码来实现这个需求:1.配置文件确保配置文件导出的是一个reactive对象:import{reactive}from'vue';exportconst......
  • 深入理解 Vue 3 组件通信
    在Vue3中,组件通信是一个关键的概念,它允许我们在组件之间传递数据和事件。本文将介绍几种常见的Vue3组件通信方法,包括props、emits、provide和inject、事件总线以及Vuex状态管理。1.使用props和emits进行父子组件通信props传递数据props是父组件向子组件传递......
  • 基于注解方式组件管理
    基于注解方式组件管理之前是通过在xml文件中向ioc容器中配置bean,通过<bean标签的方式,注解的方式是在Java类上使用注解标记某个类,将该类配置到ioc容器。主要分成两步:在类上使用注解让ioc识别那些类加了注解1.注解的ioc配置spring提供了以下几个注解,直接标记在类上,把他......
  • iOS开发基础133-GCD相关
    先看一段代码,这是项目中图片上传的一部分代码。//开启线程组上传图片dispatch_group_tgroup=dispatch_group_create();[self.selectedPhotosenumerateObjectsUsingBlock:^(UIImage*_Nonnullobj,NSUIntegeridx,BOOL*_Nonnullstop){dispatch_gro......