您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關怎么樣解決混亂的頁面彈窗,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
對于一些快速迭代的產品來說,特別是移動端 C端產品,基于用戶運營的目的,在 app首頁給用戶展示各種各樣的彈窗是很常見的事情,在產品初期,由于迭代版本和運營策略變化地還不是太大,所以可能覺得沒什么,但當產品運營到后期,各種八竿子打不著的運營策略輪番上陣,彈窗的樣式、邏輯等都變了不知道多少遍的時候,問題就出來了
由于前期沒有做好規劃,首頁的彈窗組件可能放了十多個甚至更多,不僅是首頁有,首頁內又引入了十多個個子組件,這些子組件內也有,搞不好這些子組件內還有子組件,子組件的子組件同樣還有彈窗,每個彈窗都有對應的一組控制顯隱邏輯,分散在多個組件多個方法中,但是首頁只有一個頁面,你不可能讓所有符合顯示條件的彈窗,全都一下子彈出來,反正我是沒見過這么做的 app,那么如何管理這些彈窗就成了頭等大事
而往往當你意識到這一點的時候,很可能也正是局勢發展到無法控制的時候 治不了,等死吧
場景:A彈窗和 B彈窗位于主組件內,C彈窗位于主組件的子組件 C中,D彈窗位于主組件的子組件 B中,E彈窗位于主組件的子組件F的子組件G中
PM:我希望剛進入這個頁面的時候,只有當 A彈窗 和 B彈窗以及 C彈窗,都不展示的時候,才展示 D彈窗,如果 D彈窗展示過了,除非 B彈窗之后又展示了一遍,否則無論什么情況下都不展示 E彈窗
FE:???
稍加思考一下,其實這件事情并不難辦,交給后端通過接口控制所有彈窗的顯隱就行了 主要是架構的提前規劃,以及低耦合的代碼邏輯
彈窗的配置化
先確定一個大體思路,首先,必須要明確地知道當前頁面共有哪些彈窗組件,包括頁面的子組件以及子組件的子組件內的彈窗組件,這是必須的,否則你連有哪些組件都不知道怎么精確控制?
所以,還是上面那句話,提前規劃,防患于未然是很重要的,不然等頁面迭代了幾十版,當初寫代碼的人都不在了你才想到去統計一下頁面上到底有多少個彈窗,那真是夠你喝一壺的
那么就需要在一個地方統一把這些彈窗全記錄下來,方便管理,于是可以得到下面這種數據結構:
// modalMap.js export default { // 記錄首頁 index頁面內的彈窗項 index: { modalList: [{ name: 'modal_1', level: 10, show: true }, { name: 'modal_2', level: 22, show: true }, { name: 'modal_3', level: 70, show: true }], children: { child1: { modalList: [{ name: 'modal_1_1', level: 8, show: true }, { name: 'modal_1_2', level: 62, show: true }], children: { child1_1: { modalList: [{ name: 'modal_1_1_1', level: 8, show: true }, { name: 'modal_1_1_2', level: 60, show: true }] } } } } } // ...還可以繼續記錄其他頁面的彈窗結構 }
modalMap.js文件記錄每個頁面內所有的彈窗項,例如,首頁 index內的彈窗項都在屬性名index對于的值數據結構中,index這個頁面主組件內存在兩個 modal,可以分別命名為 modal_1和 modal_2,如果 index這個頁面主組件的子組件內也有 modal,則繼續嵌套,例如,index主組件的子組件 child1中也有 modal,那么就把 child1放到 index的 children中繼續記錄,以此類推
這種結構看起來比較清晰,主組件及主組件內的子組件內的 modal都很清晰,一目了然,當然,你可以不用這種結構,完全取決于你,這里就暫時這么定義
每個 modal除了 name之外,還有 level 和 show屬性
level 用于標識當前 modal的層級,每個頁面正常只能同時展示一個 modal,但如果有多個 modal都同一時間都滿足展示的條件,則對比它們的 level值,哪個大就優先展示哪個,其余的忽略掉,杜絕一個頁面可能提示展示多個彈窗的情況;
show屬性則是在 modal內部來決定 modal最終是否展示,這樣一來就可以無視外界條件,很輕松地通過配置來禁止掉彈窗的顯示
通過發布/訂閱模式來管理彈窗
彈窗的配置結構已經確定了,下一步就是對這些配置的管理了
一般情況下,多個頁面同時滿足條件需要進行展示的場景,大多數都是發生在剛進入頁面,頁面發出多個請求,這些請求的返回結果分別控制對應的一個彈窗的展示
因為發出去的這些請求很可能分屬于不同的業務線或部門管轄,相互獨立,所以說如果把彈窗的控制權交給后端來做,其實是有點困難的,再加上請求是異步的,前端想要用意大利面條式代碼來保證彈窗之間的互斥性也不太容易,綜合起來,也就導致了當頁面上迭代出了數十個以上彈窗的時候,如果沒有提前規劃好,還是很容易出現彈窗同時展示的問題的
這里暫時就以剛進入頁面的情況為例,進行邏輯梳理
首先,我需要知道頁面上有哪些彈窗可能會在剛進入頁面的時候彈出來(即通過接口控制單個彈窗的展現與否),然后在所有彈窗的數據都拿到了的時候(即跟彈窗相關的接口都已經返回數據),才進行彈窗的展示
這種情況比較適合使用發布/訂閱者模式,單個接口的數據返回就是一個訂閱,當所有接口都訂閱之后,就進行發布,也就是彈窗展示
// modalManage.js class ModalManage { constructor (modalList) { this.modalFlatMap = {} this.modalList = modalList } // ... }
通過 ModalManage類來管理彈窗,此類在初始化時接收一個參數 modalList,這個參數其實就是剛進入頁面時,頁面上所有可能展示的彈窗(包括子組件的彈窗)的名稱集合,也就是必須要知道頁面上到底有多少個可能同時展示的彈窗,以上述示例代碼 modalMap.js為例, index頁面的 modalList值就是 ['modal_1', 'modal_2', 'modal_3', 'modal_1_1', 'modal_1_2', 'modal_1_1_1', 'modal_1_1_2']
這里其實直接傳彈窗數量就行了,index中有 7個彈窗可能同時展示,所以可以直接傳 7,我這里之所以要傳名稱進去,實際上是為了方便調試,如果代碼出問題了,比如頁面上實際有 5個接口可以控制 5個彈窗的展示,但你卻只訂閱了 4次,如果只傳數字,你就需要一個個找過去看是哪一個忘記訂閱了,但如果傳名稱,你一下子就能調試出來,也就是代碼的可維護性會好一點
當頁面上任意一個彈窗的狀態(即是否滿足展示的條件)確定下來后,就進行訂閱操作:
// modalManage.js add (name, dataInfo) { // level, handler if (this.modalList.indexOf(name) !== -1) { if (!this.modalFlatMap[name]) { this.modalFlatMap[name] = dataInfo this.notify() } else { console.log('重復訂閱') } } else { console.log('無效訂閱') } }
this.modalFlatMap是為了記錄訂閱列表,當訂閱列表的長度和 modalList相同時,說明所有的彈窗狀態都已經準備就緒,可以根據這些彈窗的優先級進行展示了,也就是 notify方法要做的事情
notify方法中,先排除掉屬性 show為 false的彈窗項,再對比剩下的彈窗的 level,只展示 level最大的那個彈窗:
// modalManage.js notify () { if (Object.keys(this.modalFlatMap).length === this.modalList.length) { const highLevelModal = Object.keys(this.modalFlatMap).filter(key => this.modalFlatMap[key].show).reduce((t, c) => { return this.modalFlatMap[c].level > t.level ? this.modalFlatMap[c] : t // 這個 { level: -1 } 只是為了給 reduce函數一個 initialValue,modal項的 level都應該大于這個 initialValue的 level值,即 -1 }, { level: -1 }) highLevelModal.handler() } }
使用單例模式管理嵌套組件以及多個頁面的彈窗
上述的 ModalManage類已經足以管理彈窗了,但還有個問題,如果一個頁面上的彈窗,分散位于頁面主組件及其子組件,甚至是子組件的子組件內,怎么辦?
這個時候就需要使用單例了
// 單例管理 const manageTypeMap = {} // 獲取單例 function createModalManage (type) { if (!manageTypeMap[type]) { manageTypeMap[type] = new ModalManage(getAllModalList(modalMap[type])) } return manageTypeMap[type] }
通過 createModalManage這個方法來創建 ModalManage實例,根據傳入的 type來決定是否創建新的實例,如果單例管理對象 manageTypeMap中不存在 type對于的實例,則 new一個 ModalManage實例,存入 manageTypeMap中,并返回這個新實例,否則就返回 manageTypeMap中已經創建好了的實例
這樣一來,無論彈窗分散在多少個組件內,無論這些組件嵌套得有多深,都能夠在保證代碼低耦合的前提下,順利地訂閱/發布事件
這里的 getAllModalList方法是個工具方法,用于從 modalMap中獲取頁面對應的彈窗數據結構:
// util.js const getAllModalList = modalInfo => { let currentList = [] if (modalInfo.modalList) { currentList = currentList.concat( modalInfo.modalList.reduce((t, c) => t.concat(c.name), []) ) } if (modalInfo.children) { currentList = currentList.concat( Object.keys(modalInfo.children).reduce((t, c) => { return t.concat(getAllModalList(modalInfo.children[c])) }, []) ) } return currentList }
至于 createModalManage的參數type,其值可以就是一個字符串,例如如果需要管理首頁 index上可能同時展示的所有的彈窗,則可以將 type 的值指定為 index,在 index主組件以及其包含彈窗的子組件內,都通過這個字段來獲取 ModalManage單例對象:
const modalManage = createModalManage('index')
這樣做同時也解決了另外一個問題,就是多個頁面的彈窗管理問題,index頁面通過 index創建 ModalManage單例,詳情頁就可以通過 detail來創建 ModalManage單例,雙方互不干擾
本文只是對彈窗這么一種具體的案例進行分析,實際上應用于其他場景,例如頁面同一個位置的懸浮掛件管理等都是可行的
無論是彈窗的管理還是掛件的管理,放在 mvvm框架中,都是數據的管理,主流前端框架對于復雜的數據管理,都已經有對應的解決方案,例如 vuex 和 redux等,這些解決方案當然也能夠解決上面的問題
上述就是小編為大家分享的怎么樣解決混亂的頁面彈窗了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。