作為一個后臺管理系統(tǒng),對多頁簽的支持必不可少,之前我對
ElementUI的彈窗樣式稍加改造,配合上append-to-body相關屬性,使對話框能夠內嵌到各個頁簽之中。當我以為大功告成暗中竊喜之時,拿起鼠標噼里啪啦一陣狂點,才發(fā)現(xiàn)自己還是太天真,果然我們只是從一個坑跳到了另外一個坑(見下方的動態(tài)圖)。為了理想中的康莊大道,這場填坑之旅勢在必行


0x01 坑從哪里來
要想解決問題,我們必須得知道問題的根源所在,才能一勞永逸。而途徑只有一個,那就是閱讀源碼。通過源碼,我們知道了彈框的遮罩層是由一個名為popup的mixin混入工具類進行操作的,而popup的核心在于PopupManager這個對象。其中關鍵部分截圖如下:
![]() dialog/src/component.vue
|
![]() popup/index.js
|
![]() popup/popup-manager.js
|
|---|---|---|
![]() popup/popup-manager.js
|
![]() popup/popup-manager.js
|
原來,Dialog組件在全局只維護一個遮罩層DOM節(jié)點,然后在關閉當前遮罩層的時候通過堆棧恢復上一個遮罩層位置。以截圖為例,當我們打開第二個頁簽的彈窗時其實是把第一個彈窗的遮罩層給強行征用了,所以當我們切換回第一個頁簽時,背景就莫名的消失了。當我們關閉第二個彈窗時,堆棧恢復了上一個彈窗的位置所以我們造成了彈窗未關閉的假象。顯然,這種節(jié)省資源的做法在多頁簽及彈窗內嵌的需求下必定是先天不足,分身乏術。
0x02 填坑思路
理清了癥結我們對癥下藥即可,而藥方也很簡單,就是舍得下本,給每個彈窗分配一個遮罩。
其實通過上述截圖我們就可以發(fā)現(xiàn),遮罩都是由PopupManager進行管理的,因此我們只需修改PopupManager的源碼即可,總結起來就是對其中的getModal和modalStack進行改造,使其能夠根據不同的對話框(每個對話框都維護著一個不同的id,可由此區(qū)分)對不同的遮罩層進行操作。具體實現(xiàn)再次不再多說。
這里有個難點在于對esc鍵的響應,由于對于按鍵的監(jiān)聽操作是在整個文檔,無法與單個遮罩進行關聯(lián)處理(當然了,如果只需要對遮罩范圍內進行監(jiān)聽,那對每個遮罩都綁定一個監(jiān)聽事件也未嘗不可,這個問題自然也就不存在了)。在此的思路是遍歷所有的遮罩節(jié)點,取出在當前界面上可視的并且zIndex最高的一個遮罩進行處理,在此我們借助于window.getComputedStyle的力量來判斷對應遮罩是否是我們想要關閉的。
0x03 填坑方法
現(xiàn)在我們已經有了填坑的思路,但是要怎么填,把這個思路付諸實踐還是值得思考的,這里提供三種方法:
- 對Element庫進行修改替換。即到github上下載最新的官方庫進行修改,然后編譯打包,對項目的依賴改成本地編譯后的庫即可
缺點:動作太大,不利更新 - 局部替換。增對我們需要修改的組件進行局部覆蓋替換。以
dialog組件為例,將dialog組件的源碼copy到項目目錄下,對其中的Popup的引用替換成我們修改后的popup工具類即可
缺點:得對所有用到遮罩的組件(包括Drawer,Message等)進行替換,否則官方的遮罩和自定義的遮罩同時使用沖突造成zIndex不正常的情況 - 直接對
popup-manager的行為進行覆蓋
這種是我比較青睞的方式,修改粒度也比較小。思路是在自定義的PopupManager中引入(import)官方組件的PopupManager,然后對其默認行為(函數)進行覆蓋重寫。




