vue usePop彈窗控制器的實(shí)現(xiàn)
當(dāng)UI庫彈窗無法滿足自定義需求時(shí),需要我們自己開發(fā)簡單的彈窗組件。彈窗組件與普通業(yè)務(wù)組件開發(fā)沒有太大區(qū)別,重點(diǎn)在多彈窗之間的關(guān)系控制。例如: 彈窗1,彈窗2 由于觸發(fā)時(shí)機(jī)不同,需要不同的層疊關(guān)系,后觸發(fā)的始終在最前端,點(diǎn)擊彈窗頭改變層疊關(guān)系。 單一彈窗多處調(diào)用等。這里封裝基礎(chǔ)的管理鉤子,簡化這些問題的處理。
功能目標(biāo)
- 單例,多例彈窗
- 可配置彈窗自定義參數(shù)
- 可接收彈窗自定義事件
- 層級(jí)控制
- 自定義定位
該鉤子的目的主要為了處理彈窗之間的控制關(guān)系,具體如何渲染交由調(diào)用方
快速使用
// 主容器 import { usePopContainer, buildDefaultPopBind, position } from '@/hooks/usePop' import UserInfoPop form './UserInfoPop.vue' // 快捷工具,將內(nèi)部鉤子通過依賴注入,共享給子組件 const [popMap, popTools] = usePopContainer() const popBind = buildDefaultPopBind(popTools, popTools.componentsCache) const userPop = popBind('userInfo', UserInfoPop, { position: { // 組件定位 top: 200 }, userId: 'xxx', // 組件porps @close(){ // 組件事件 console.log('close') } }) // 調(diào)用 userPop.open() setTimeout(userPop.close, 1000 * 3) // template <template v-for="(pop, popId) of popMap"> // 渲染彈窗列表 <component :is="pop.component" :key="popId" v-bind="pop.props" v-on="pop.on" > </component> </template>
多處調(diào)用
同一彈窗,多實(shí)例 add
// 容器注冊(cè) const [popMap, popTools] = usePopContainer() // 新增彈窗1 popTools.add(popId1, { component: UserPop, // 彈窗組件 useId: 'xxx', // 彈窗Props '@close': () => { ... } //彈窗事件 }) // 新增彈窗2 popTools.add(popId2, { component: UserPop, // 彈窗組件 useId: 'xxx', // 彈窗Props '@close': () => { ... } //彈窗事件 }) // 覆蓋彈窗1 // popId 為彈窗唯一標(biāo)識(shí), 如果popId相同,組件配置將被替換 popTools.add(popId1, { component: UserPop, // 彈窗組件 useId: 'yyy', // 彈窗Props '@close': () => { ... } //彈窗事件 })
所有彈窗都通過popId,查找or判斷唯一性。
配置參數(shù):以
@
開頭的都將組為組件的事件被綁定, 除了@[事件名]
component
其他屬性都將作為props,包括 style 等屬性
移除 remove
const [popMap, popTools] = usePopContainer() popTools.add(popId, { component: UserPop, // 彈窗組件 useId: 'xxx', // 彈窗Props '@close': () => { ... } //彈窗事件 }) // 移除 popTools.remove(popId)
替換 replace
// 主容器 const [popMap, popTools] = usePopContainer() // 子組件A popTools.replace(popId, { component: UserPop, // 彈窗組件 useId: 'xxx', // 彈窗Props '@close': () => { ... } //彈窗事件 }) // 子組件B popTools.replace(popId, { component: UserPop, // 彈窗組件 useId: 'xxx', // 彈窗Props '@close': () => { ... } //彈窗事件 })
當(dāng)有多處調(diào)用同一彈窗,而只需要最新的觸發(fā)彈窗時(shí),使用 replace. 該方法其實(shí)就是
remove
add
的包裝方法
更新 update
const [popMap, popTools] = usePopContainer() popTools.replace(popId, { component: UserPop, // 彈窗組件 useId: 'xxx', // 彈窗Props '@close': () => { ... } //彈窗事件 }) // 更新參數(shù) popTools.update(popId, { useId: 'yyy' })
通過popId 查詢彈窗,將新傳入的參數(shù)與原配置做合并
預(yù)注冊(cè) componentsCache
const [popMap, popTools] = usePopContainer() // 局部預(yù)先注冊(cè) // 注冊(cè)只是預(yù)先緩存 popTools.componentsCache.add(popId, { component: UserPop, id: 'xxx', '@cloes': () => {...} }) // 調(diào)用 popTools.add(popId, { component: popId }) // of popTools.replace(popId, { component: popId }) // add將從componentsCache查詢預(yù)注冊(cè)配置
除了局部緩存, componentsCache, 模塊還導(dǎo)出了 globalComponentsCache 全局公共緩存。
依賴注入
為了方便父子組件調(diào)用,提供了usePopContainer
usePopChildren
方法,
// 父組件 const [popMap, popTools] = usePopContainer() // 子組件 const { popTools } = usePopChildren() popTools.add({ ... })
函數(shù)接收依賴注入標(biāo)識(shí), 為傳入標(biāo)識(shí)時(shí),使用默認(rèn)標(biāo)識(shí)
usePop 工具函數(shù)
- add(popId, options) 創(chuàng)建彈窗
- update(popId, options) 更新彈窗配置(定位, props,events)
- remove(popId) 移除彈窗
- replace(popId, options) 替換,如果多處調(diào)用同一彈窗,希望只顯示唯一同類彈窗時(shí),
使用該函數(shù),多個(gè)彈窗公用相同的popId
- clearAllPop() 清空所有彈窗
- updateIndex(popId) 更新彈窗層級(jí)
- downIndex(popId) 層級(jí)下降一級(jí)
- topIndex(popId) 層級(jí)置頂
core 實(shí)現(xiàn)
import { shallowRef, unref, provide, inject } from 'vue' import { merge } from 'lodash-es' import { splitProps, counter } from './utils' export const DEFAULT_POP_SIGN = 'DEFAULT_POP_SIGN' // 全局層級(jí)累加器 export const counterStore = counter() /** * 預(yù)先pop注冊(cè)表 * @summary * 便捷多處pop調(diào)用, 調(diào)用pop顯示方法時(shí), * 直接通過名稱查詢對(duì)應(yīng)的組件預(yù)設(shè) * 將調(diào)用與事件配置解耦 * @returns */ function componentsRegistry () { let componentsCache = new Map([]) function has (componentName) { return componentsCache.has(componentName) } function add (componentName, options) { componentsCache.set(componentName, options) } function remove (componentName) { if (has(componentName)) { componentsCache.delete(componentName) } } function fined (componentName) { return componentsCache.get(componentName) } function clear () { componentsCache = new Map([]) } function getComponents () { return [...componentsCache.values()] } function getComponentNames () { return [...componentsCache.keys()] } return { has, add, remove, fined, clear, getComponents, getComponentNames } } export const globalComponentsCache = componentsRegistry() /** * 彈窗控制器 * @summary * 提供多彈窗控制邏輯: * 1. 單例, 多例: 通過不同的 popId 控制彈窗實(shí)例的個(gè)數(shù) * 2. 參數(shù)接收: open接收初始傳給pop的事件和參數(shù)配置, update 提供參數(shù)更新 * 3. 事件回調(diào): options 配置屬性 { @[事件名稱]:事件回調(diào) } 將作為事件綁定到pop上 * 4. 動(dòng)態(tài)疊加: 內(nèi)部將為組件配置 zIndex, 組件內(nèi)需要自定義接收該參數(shù),判斷如何處理層疊關(guān)系 * 5. 定位: 定位需要彈窗組件接收 position props 內(nèi)部綁定樣式 * * @tips * 這里定位為了兼容 useMove做了接口調(diào)整,原接口直接輸出定位樣式。當(dāng)前出position屬性, * 組件內(nèi)需要自行處理定位樣式。 這里存在 style 合并和透傳的的問題, 通過透傳的style與 * props 內(nèi)定義的style將分開處理, 即最終的結(jié)果時(shí)兩個(gè)style的集合, 且透傳的style優(yōu)先級(jí)高于 * prop。所以如果直出定位樣式,通過透傳綁定給彈窗組件,后續(xù)的useMove拖拽樣式將始終被透傳樣式覆蓋 * * @api * - add(popId, options) 創(chuàng)建彈窗 * - update(popId, options) 更新彈窗配置(定位, props,events) * - remove(popId) 移除彈窗 * - replace(popId, options) 替換,如果多處調(diào)用同一彈窗,希望只顯示唯一同類彈窗時(shí), * 使用該函數(shù),多個(gè)彈窗公用相同的popId * - clearAllPop() 清空所有彈窗 * - updateIndex(popId) 更新彈窗層級(jí) * - downIndex(popId) 層級(jí)下降一級(jí) * - topIndex(popId) 層級(jí)置頂 * * @example01 - 一般使用 * * const [ * pops, * popTools * ] = usePop() * * * // 容器組件 * <component * v-for='(pop, popId) of pops' * :is='pop' * v-bind='pop.props' // 接收定位樣式 * v-on='pop.on' // 接收回調(diào)事件 * :key='popId'> * </component> * * // 調(diào)用彈窗 * popTools.add('popId', { * component: POP, // 彈窗組件 * position: { top: 200 } // 彈窗定位 * title: 'xxx', // 彈窗自定義props * @click(e){ // 彈窗事件 * .... * } * }) * * * @example02 - 預(yù)注冊(cè) * 通過預(yù)注冊(cè)組件,再次調(diào)用時(shí),只需要傳入對(duì)應(yīng)注冊(cè)名稱,而不需要具體的配置項(xiàng) * const [ pops, popTools ] = usePop() * * // 注冊(cè)本地彈窗 * popTools.componentsCache.add('userInfo', { * component: CMP, * opsition: { ... } * ... * }) * * // 調(diào)用 * popTools.add('userInfo', { component: 'userInfo' }) * */ export function usePop () { const components = shallowRef({}) const componentsCache = componentsRegistry() function has (popId) { return !!unref(components)[popId] } /** * 添加pop * @param popId * @param options * @returns */ function add (popId, options = {}) { if (has(popId)) { return false } let { component, ..._options } = options // 全局緩存 if (globalComponentsCache.has(component)) { const { component: cacheComponents, ...cacheOptions } = globalComponentsCache.fined(component) component = cacheComponents _options = { ...cacheOptions, ..._options } } // 局部緩存 if (componentsCache.has(component)) { const { component: cacheComponents, ...cacheOptions } = componentsCache.fined(component) component = cacheComponents _options = { ...cacheOptions, ..._options } } counterStore.add() const newOptions = splitProps({ ..._options, zIndex: counterStore.getCount() }) components.value = { ...components.value, [popId]: { popId, component, ...newOptions } } } /** * 更新組件參數(shù) * @param {*} popId * @param {*} options * @returns */ function update (popId, options = {}) { if (!has(popId)) { return false } const { component, ...oldOptions } = components.value[popId] const newOptions = splitProps(options) components.value = { ...components.value, [popId]: { component, ...merge(oldOptions, newOptions) } } } /** * 移除pop * @param popId */ function remove (popId) { if (has(popId)) { const newCmp = components.value delete newCmp[popId] components.value = { ...newCmp } } } /** * 多處調(diào)用同一pop時(shí), 替換原顯示pop。 * @param popId * @param options */ function replace (popId, options) { remove(popId) add(popId, options) } function clearAllPop () { components.value = {} } /** * 向上一層級(jí) * @param popId * @returns */ function updateIndex (popId) { if (!has(popId)) { return } const currentComponent = unref(components)[popId] const upComponent = Object.values(unref(components)).fined(i => i.zIndex > currentComponent.zIndex) const currentIndex = currentComponent.zIndex const upIndex = upComponent.zIndex update(currentIndex.popId, { zIndex: upIndex }) update(upComponent.popId, { zIndex: currentIndex }) } /** * 向下一層級(jí) * @param {*} popId * @returns */ function downIndex (popId) { if (!has(popId)) { return } const currentComponent = unref(components)[popId] const upComponent = Object.values(unref(components)).fined(i => i.zIndex < currentComponent.zIndex) const currentIndex = currentComponent.zIndex const upIndex = upComponent.zIndex update(currentIndex.popId, { zIndex: upIndex }) update(upComponent.popId, { zIndex: currentIndex }) } /** * 頂層 * @param popId * @returns */ function topIndex (popId) { if (!has(popId)) { return } counterStore.add() update(popId, { zIndex: counterStore.getCount() }) } return [ components, { has, add, remove, update, replace, clearAllPop, topIndex, updateIndex, downIndex, componentsCache } ] } /** * 嵌套結(jié)構(gòu)下的彈窗鉤子 */ // 容器鉤子 export function usePopContainer (provideKey = DEFAULT_POP_SIGN) { const [popMap, popTools] = usePop() provide( provideKey, { popTools } ) return [ popMap, popTools ] } // 子容器鉤子 export function usePopChildren (provideKey = DEFAULT_POP_SIGN) { return inject(provideKey, {}) }
core utils
export function isEvent (propName) { const rule = /^@/i return rule.test(propName) } // @click => click export function eventNameTransition (name) { return name.replace('@', '') } // 拆分事件與屬性 export function splitProps (cmpProps) { return Object.entries(cmpProps).reduce((acc, [propName, propValue]) => { if (isEvent(propName)) { // 自定義事件 acc.on[eventNameTransition(propName)] = propValue } else { acc.props[propName] = propValue } return acc }, { on: {}, props: {} }) } export function counter (initCount = 0, step = 1) { let count = initCount function add (customStep) { count += customStep || step } function reduce (customStep) { count -= customStep || step } function reset (customStep) { count = customStep || initCount } function getCount () { return count } return { add, reduce, reset, getCount } }
工具
import { merge } from 'lodash-es' /** * 注冊(cè)并返回彈窗快捷方法 * @param {*} popTools * @returns */ export function buildDefaultPopBind (popTools, componentsCache) { return (popId, component, options) => { componentsCache.add(popId, { component, // 默認(rèn)定位 position: position(), ...bindDefaultEvents(popTools, popId), ...options }) return { open (options) { popTools.add(popId, { component: popId, ...options }) }, close () { popTools.remove(popId) }, update (options) { popTools.update(popId, { component: popId, ...options }) }, replace (options) { popTools.replace(popId, { component: popId, ...options }) } } } } export const DEFAULT_POSITION = { top: 240, left: 0, right: 0 } export function position (options = DEFAULT_POSITION) { return merge({}, DEFAULT_POSITION, options) } export function bindDefaultEvents (popTools, popId) { return { '@headerMousedown' () { popTools.topIndex(popId) }, '@close' (e) { popTools.remove(popId) } } }
到此這篇關(guān)于vue usePop彈窗控制器的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)vue usePop彈窗控制器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VUE中v-on:click事件中獲取當(dāng)前dom元素的代碼
這篇文章主要介紹了VUE中v-on:click事件中獲取當(dāng)前dom元素的代碼,文中同時(shí)給大家提到了v-on:click獲取當(dāng)前事件對(duì)象元素的方法,需要的朋友可以參考下2018-08-08Pure admin-Router標(biāo)簽頁配置與頁面持久化實(shí)現(xiàn)方法詳解
這篇文章主要介紹了Pure admin-Router標(biāo)簽頁配置與頁面持久化實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-01-01解決VUEX刷新的時(shí)候出現(xiàn)數(shù)據(jù)消失
這篇文章主要介紹了解決VUEX刷新的時(shí)候出現(xiàn)數(shù)據(jù)消失,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07Ant Design的可編輯Tree的實(shí)現(xiàn)操作
這篇文章主要介紹了Ant Design的可編輯Tree的實(shí)現(xiàn)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-10-10Vue實(shí)現(xiàn)項(xiàng)目部署到非根目錄及解決刷新頁面時(shí)找不到資源
這篇文章主要介紹了Vue實(shí)現(xiàn)項(xiàng)目部署到非根目錄及解決刷新頁面時(shí)找不到資源問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03