IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    大厂前端架构实战之modal模态对话框管理

    卢见特man发表于 2023-07-10 15:20:05
    love 0

    前言

    这是我新开的一个关于前端知识的模块,主要讲解日常开发中常用的前端技巧。此模块讲解的知识都是能大幅提升前端性能的奇技淫巧,供大家日常交流,同时也希望大家不吝赐教,指出本人的不足。

    本文使用的技术栈: vue3 + typescript + pinia + element-plus

    背景

    modal模态框又称之为弹出框、对话框,是前端日常开发最为频繁的组件之一。但是在日常开发过程中经常遇到一些性能、或者写法繁琐的问题。下面是我举例的一些常见问题:

    1.modal框塞入当前页面会导致整个组件更新,影响性能

    <template>  
        <div class="about">    
        <el-button class="todo-list--add-btn" @click="showModal">      
            添加任务    
        </el-button>    
        <ul>      
            <li 
                class="todo-list--item-box"       
                v-for="(item, index) in list"        
                :key="item.title">        
                <div>{{ item.title }}</div>        
                <div class="todo-list--add-btn" @click="handleDelToDoList(index)">          
                删除        
                </div>      
             </li>    
         </ul>
    // 此种方式更新会带动组件的更新   
        <el-dialog :model-value="show" title="添加任务" width="30%">    
            <el-input v-model="todoListAddInput" placeholder="请输入" />    
            <template #footer>      
                <span class="dialog-footer">        
                    <el-button @click="modalStore.hideModal(ModalType.TODO_LIST)">         
                    取消        
                    </el-button>        
                    <el-button type="primary" @click="handleAddToDolist"> 提交 </el-button>      
                </span>    
            </template> 
        </el-dialog>  
    </div>
    </template>
    1. 许多后台应用的modal框体量都是非常庞大的,一些流程modal框动辄成百上千行代码很正常,如果在首次加载势必影响首次加载耗时
    2. vue使用teleport,react使用createPortal把modal框放到根节点是一些公司的主要优化手段,但是没有全局注册调用事件,导致modal框管理混乱,前端开发人员开发的modal框组件遍布项目各个文件夹

    下面我就针对以上问题做出解决方案

    结构示意图:

     

    全局注册modal框

    使用pinia进行modal的全局管理不仅能使各个组件很方便的调用model框,还能统一modal入口,方便管理。

    // 全局modal框唯一身份标识
    export enum ModalType {  TODO_LIST,}
    
    // modal框所需要的参数注册接口
    export interface ModalDataType {  
        [ModalType.TODO_LIST]: {   
            onSubmit: (value: string) => void;  
        }
    ;}
    
    // modal框实例对象接口
    interface ModalInstance<T extends ModalType> {  
        type: T; // modal框标识 
        component: any; // modal框vue组件  
        data?: ModalDataType[T]; // modal框传值  
        onEnter?: (data: ModalDataType[T]) => void; // 生命周期钩子,在modal框显示之后调用  
        onExit?: (data: ModalDataType[T]) => void; // 生命周期钩子,在modal框显隐藏之后调用
    }
    
    type ModalListItem = { [key in ModalType]: ModalInstance<ModalType> };

    通过以上代码我们就设计好了全局modal框的所用到的属性。通过对这些属性的运用,我们可以很方便的建立起一个全局数据中心,这一步解决了modal框的管理问题(具体代码详见文章结尾GitHub地址):

    useModalStore = {
      //modal注册中心,当有新modal被打开时,把modal实例注册到此对象
      modalInstanceMap: Ref<ModalListItem>  
      //当前打开的modal的集合
      showModalList: Ref<ModalType>  
      //modal框开启函数,如果当前打开的modal未被注册,showModal将注册此组件
      showModal: <T extends ModalType>(modalType: T, data?: ModalDataType[T] | undefined) => void  
      //modal框隐藏函数   
      hideModal:(modalType: ModalType) => void  
      //注册modal框生命周期钩子onEnter
      onEnter:<T extends ModalType>(modalType: T, fn: (data: ModalDataType[T]) => void) => void   
      //注册modal框生命周期钩子onExit  
      onExit:<T extends ModalType>(modalType: T, fn: (data: ModalDataType[T]) => void) => void
    }

    全局挂载modal框

    建立起全局数据中心,我们下一步就是全局注册modal框,这一步解决了我们先前所提到的modal框在其他组件内部注册modal框,影响性能的问题:

    // app.ts
    
    <script>
    import { useModalStore } from "@/stores/modal";
    const modalStore = useModalStore();
    
    
    <template>
    ...
    ...
        <component    
            v-for="item in modalStore.modalList"      
            :key="item.type"      
            :is="item.component" 
        />
    ...

    modal框实例创建

    这一步我们创建所需的对应的模态框,创建步骤为以下流程

    1. modal全局数据中心注册modal组件(新建一个ModalType枚举,定义modal组件传参,引入modal组件并注册)

       2.编写modal组件,注册modal组件的显示隐藏函数

    // /modal/todolist.vue  
    
    import { ModalType, useModalStore, type ModalDataType } from "@/stores/modal";
    import { computed, ref } from "vue";
    
    const modalStore = useModalStore();
    const todoListAddInput = ref();
    
    //控制modal的显示隐藏
    const show = computed(() =>  modalStore.showModalList.includes(ModalType.TODO_LIST));
    1. 注册生命周期钩子,此钩子可以拿到传入的参数,并且因为做了泛型处理,data的属性为对应的ModalType的传参类型(具体可以访问文章后面的github地址感受一下)
    modalStore.onEnter(ModalType.TODO_LIST, (data) => {  
        props = data
    });
    
    modalStore.onExit(ModalType.TODO_LIST, () => {  
        todoListAddInput.value = ""
    });

    调用modal框

    // todolist.vue
    <script>
    import { useModalStore, ModalType } from "@/stores/modal";
    
    interface TodoList {  
        title: string;
    }
       
    const modalStore = useModalStore();
    function showModal() {  
       modalStore.showModal(ModalType.TODO_LIST, {    
           onSubmit: // 自定义提交函数
       })
    }

    这样我们就可以很方便的在各个组件调用modal框了,

    结尾

    致力于代码层面的优化,提高自身编程能力。通过上述优化操作,解决了modal的大部分痛点,希望能够帮助到你。

    项目地址:https://github.com/charCR2/vue-repo/tree/main/modal-example(记得给star哈~,感谢)



沪ICP备19023445号-2号
友情链接