龙空技术网

基于elementUI的popupjs开发移动端组件(一)

小屹的喵生活 482

前言:

此时我们对“js中的pop方法”大概比较注重,大家都需要剖析一些“js中的pop方法”的相关内容。那么小编也在网摘上汇集了一些关于“js中的pop方法””的相关资讯,希望我们能喜欢,兄弟们一起来了解一下吧!

本文来自我的小伙伴晨荒,全网首发

场景:平常移动端项目中,总是会遇到一些对话框,作用安卓端用户,我希望通过返回键能直接关闭对话框,但是不要退出当前页面,更贴近原生APP的操作方式

对此,笔者发现,通过h5的historyState可以解决此类问题。

在进行操作的时候,我们在history里面推入一条记录,进行返回的时候,我们可以通过window.onpoostate事件来监控,关闭对话框。

借用elementUI的popupjs管理自己的弹窗

element源码 src/utils/popup 文件夹下存放了element的弹窗管理器

​ 用过elementUI的同学不难发现,框架下的每一个对话框,都有一个z-index,且实时变化,每一个对话框都是后来居上

下面我们梳理一下这个管理器

//popup-manager.js(有删减)//作用就是,把遮罩层抽离出各个组件,做一个统一管理let hasModal = false;let hasInitZIndex = false;let zIndex;const instances = {};const PopupManager = {  modalFade: true,  //遮罩层动画  //获取当前实例  getInstance: function(id) {    return instances[id];  },  //注册实例,每个弹窗组件beforeCreate事件中,会生成一个popId ,并调用此方法  register: function(id, instance) {    if (id && instance) {      instances[id] = instance;    }  },  //注销实例  deregister: function(id) {    if (id) {      instances[id] = null;      delete instances[id];    }  },	  //获取最新的Z-index值,每次创建新组件或者弹窗展现时会获取并使用  nextZIndex: function() {    return PopupManager.zIndex++;  },  //每个实例 如果有配置灰色遮罩层,会在此储存一条记录信息  modalStack: [],  //遮罩层点击事件  doOnModalClick: function() {    const topItem = PopupManager.modalStack[PopupManager.modalStack.length - 1];    if (!topItem) return;    const instance = PopupManager.getInstance(topItem.id);    if (instance && instance.closeOnClickModal) {      instance.close();    }  },  // 打开或创建遮罩层  openModal: function(id, zIndex, dom, modalClass, modalFade) {    if (!id || zIndex === undefined) return;    this.modalFade = modalFade;    const modalStack = this.modalStack;	// 查询对应popip的遮罩层是否已经显示     for (let i = 0, j = modalStack.length; i < j; i++) {      const item = modalStack[i];      if (item.id === id) {        return;      }    }	// 获取当前遮罩层,没有就创建 方法如下    const modalDom = getModal();	    //下面是给遮罩层添加类名       addClass(modalDom, 'v-modal');    //添加动画    if (this.modalFade && !hasModal) {      addClass(modalDom, 'v-modal-enter');    }    if (modalClass) {      let classArr = modalClass.trim().split(/\s+/);      classArr.forEach(item => addClass(modalDom, item));    }    //动画结束    setTimeout(() => {      removeClass(modalDom, 'v-modal-enter');    }, 200);    //如果方法有传dom,则添加到dom盒子中,不然则添加到body(默认)    if (dom && dom.parentNode && dom.parentNode.nodeType !== 11) {      dom.parentNode.appendChild(modalDom);    } else {      document.body.appendChild(modalDom);    }    if (zIndex) {      modalDom.style.zIndex = zIndex;    }    modalDom.tabIndex = 0;    modalDom.style.display = '';    this.modalStack.push({ id: id, zIndex: zIndex, modalClass: modalClass });  },       //关闭遮罩层  closeModal: function(id) {    const modalStack = this.modalStack;    const modalDom = getModal();    if (modalStack.length > 0) {      const topItem = modalStack[modalStack.length - 1];      if (topItem.id === id) {        if (topItem.modalClass) {          let classArr = topItem.modalClass.trim().split(/\s+/);          classArr.forEach(item => removeClass(modalDom, item));        }        modalStack.pop();        //如果还存在其他实例遮罩层,这修改zIndex为上一层的层级        if (modalStack.length > 0) {          modalDom.style.zIndex = modalStack[modalStack.length - 1].zIndex;        }      } else {        for (let i = modalStack.length - 1; i >= 0; i--) {          if (modalStack[i].id === id) {            modalStack.splice(i, 1);            break;          }        }      }    }	    // 所以遮罩层在modalStack(记录)弹出后,设置遮罩层淡出动画,并讲display设为nonde    if (modalStack.length === 0) {      if (this.modalFade) {        addClass(modalDom, 'v-modal-leave');      }      setTimeout(() => {        if (modalStack.length === 0) {          if (modalDom.parentNode) modalDom.parentNode.removeChild(modalDom);          modalDom.style.display = 'none';          PopupManager.modalDom = undefined;        }        removeClass(modalDom, 'v-modal-leave');      }, 200);    }  },      /* --------------------控制返回键添加的代码--------------------------------*/  //思路是 执行动作时添加一条记录  使用pushstate  //当记录大于一,添加记录时,改为replacestate来管理 ,目的是为了保持路由干净  historyStack:[],  pushOrReplaceState:function(id){    let instance = this.getInstance(id);    if(!instance)return ;    if (this.historyStack.length == 0) {      this.historyStack.push(this.getInstance(id))      history.pushState({        id:id      }, '', '');    } else {        this.historyStack.push(this.getInstance(id))        history.replaceState({          id: id        }, '', '');    }  },  popState:function(id){    const historyStack = this.historyStack    for (let i = 0, j = historyStack.length; i < j; i++) {      const item = historyStack[i];      if (item._popupId === id) {        history.back();        this.historyStack.splice(i,1);      }    }  }  /* ----------------------------------------------------*/};//获取一个遮罩层const getModal = function() {   // 如果管理器存在一个遮罩层 便直接返回  let modalDom = PopupManager.modalDom;  if (modalDom) {    hasModal = true;  } else {    hasModal = false;    //创建一个遮罩层 , 并为遮罩层添加事件    modalDom = document.createElement('div');    PopupManager.modalDom = modalDom;    modalDom.addEventListener('touchmove', function(event) {      event.preventDefault();      event.stopPropagation();    });	//遮罩层点击事件    modalDom.addEventListener('click', function() {      PopupManager.doOnModalClick && PopupManager.doOnModalClick();    });  }  return modalDom;};

popup-manager相当于一个中枢,每一个弹出层需要一个遮罩层,告诉我你的ID,然后openModel就可以了

至于你想知道最顶层的zIndex应该是多少,调用popupManaer.nextZIndex()便可获取

借用它,我们不用在每个对话框都添加一个遮罩层。 简单的,我们只要写设置好定位,然后通过nextZindex获取最顶层的值并设置就可以了

下面怎么开发我们的组件呢?

有的同学应该发现了,popup文件夹下还有一个index.js 。它是基于popup-manager.js,在组件各个生命周期进行操作的mixins对象

下面看部分代码(这里不贴源码了,建议读者下载源码查看)

// 这个是用于生成ID的参数,每个minxin混入的组件,都可以通过闭包拿到它便使用它let idSeed = 1;{ ..., beforeMount() {    //每次组件创前,都生产一个唯一的IP告诉popup-manager    this._popupId = 'popup-' + idSeed++;    PopupManager.register(this._popupId, this);  },  beforeDestroy() {    //组件注销    PopupManager.deregister(this._popupId);    PopupManager.closeModal(this._popupId);      },    watch:{     visible(){...           //visible  作为props值,由业务层提供     	   //为true时 进行open操作     	   //为false时 进行close操作              } ,  },        methods:{      open(options){...            //被调用的地方是 watch:visible      		//整合参数传入的配置,相当于打开遮罩前函数(options提供钩子能力)      		// 业务上主要做了一个延迟打开的逻辑            },      doOpen(){...              //主要用于打开遮罩(组件modal为true的时候)      	      //给对话框加上最新的zIndex      		  //调用onOpen钩子函数              },      doAfterOpen(){...              //修改_opening为false      		  // !!笔者在这添加了history状态代码      		  //如果组件需要通过路由改变状态              if(this.historyControl){                  PopupManager.pushOrReplaceState(this._popupId)              }                   },      close(){...              //如果组件需要通过路由改变状态      			  if(this.historyControl){                      PopupManager.popState(this._popupId)                  }              //此方法主要是关闭组件的设置方法              // 同上面open方法一样 添加了延迟逻辑             },      doClose(){...               //调用了onClose钩子函数 供业务处理               },      doAfterClose(){...      			// 主要是关闭遮罩层                    },      }}

这个js文件很简单,它为组件提供了适用popup-manager的方法,巧妙的设置钩子,让我们不用去理会时候打开或关闭遮罩层,调用设置的钩子我们就可以完成组件业务。

下期,我们将使用上面的工具方法实现一个仿VUX的对话框。。未完待续。。。谢谢您的关注

标签: #js中的pop方法