您好,登錄后才能下訂單哦!
這篇文章主要介紹“vue usePop彈窗控制器如何實現”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“vue usePop彈窗控制器如何實現”文章能幫助大家解決問題。
當UI庫彈窗無法滿足自定義需求時,需要我們自己開發簡單的彈窗組件。彈窗組件與普通業務組件開發沒有太大區別,重點在多彈窗之間的關系控制。例如: 彈窗1,彈窗2 由于觸發時機不同,需要不同的層疊關系,后觸發的始終在最前端,點擊彈窗頭改變層疊關系。 單一彈窗多處調用等。這里封裝基礎的管理鉤子,簡化這些問題的處理。
單例,多例彈窗
可配置彈窗自定義參數
可接收彈窗自定義事件
層級控制
自定義定位
該鉤子的目的主要為了處理彈窗之間的控制關系,具體如何渲染交由調用方
// 主容器 import { usePopContainer, buildDefaultPopBind, position } from '@/hooks/usePop' import UserInfoPop form './UserInfoPop.vue' // 快捷工具,將內部鉤子通過依賴注入,共享給子組件 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') } }) // 調用 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>
// 容器注冊 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 為彈窗唯一標識, 如果popId相同,組件配置將被替換 popTools.add(popId1, { component: UserPop, // 彈窗組件 useId: 'yyy', // 彈窗Props '@close': () => { ... } //彈窗事件 })
所有彈窗都通過popId,查找or判斷唯一性。
配置參數:以
@
開頭的都將組為組件的事件被綁定, 除了@[事件名]
component
其他屬性都將作為props,包括 style 等屬性
const [popMap, popTools] = usePopContainer() popTools.add(popId, { component: UserPop, // 彈窗組件 useId: 'xxx', // 彈窗Props '@close': () => { ... } //彈窗事件 }) // 移除 popTools.remove(popId)
// 主容器 const [popMap, popTools] = usePopContainer() // 子組件A popTools.replace(popId, { component: UserPop, // 彈窗組件 useId: 'xxx', // 彈窗Props '@close': () => { ... } //彈窗事件 }) // 子組件B popTools.replace(popId, { component: UserPop, // 彈窗組件 useId: 'xxx', // 彈窗Props '@close': () => { ... } //彈窗事件 })
當有多處調用同一彈窗,而只需要最新的觸發彈窗時,使用 replace. 該方法其實就是
remove
add
的包裝方法
const [popMap, popTools] = usePopContainer() popTools.replace(popId, { component: UserPop, // 彈窗組件 useId: 'xxx', // 彈窗Props '@close': () => { ... } //彈窗事件 }) // 更新參數 popTools.update(popId, { useId: 'yyy' })
通過popId 查詢彈窗,將新傳入的參數與原配置做合并
const [popMap, popTools] = usePopContainer() // 局部預先注冊 // 注冊只是預先緩存 popTools.componentsCache.add(popId, { component: UserPop, id: 'xxx', '@cloes': () => {...} }) // 調用 popTools.add(popId, { component: popId }) // of popTools.replace(popId, { component: popId }) // add將從componentsCache查詢預注冊配置
除了局部緩存, componentsCache, 模塊還導出了 globalComponentsCache 全局公共緩存。
為了方便父子組件調用,提供了usePopContainer
usePopChildren
方法,
// 父組件 const [popMap, popTools] = usePopContainer() // 子組件 const { popTools } = usePopChildren() popTools.add({ ... })
函數接收依賴注入標識, 為傳入標識時,使用默認標識
add(popId, options) 創建彈窗
update(popId, options) 更新彈窗配置(定位, props,events)
remove(popId) 移除彈窗
replace(popId, options) 替換,如果多處調用同一彈窗,希望只顯示唯一同類彈窗時,
使用該函數,多個彈窗公用相同的popId
clearAllPop() 清空所有彈窗
updateIndex(popId) 更新彈窗層級
downIndex(popId) 層級下降一級
topIndex(popId) 層級置頂
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' // 全局層級累加器 export const counterStore = counter() /** * 預先pop注冊表 * @summary * 便捷多處pop調用, 調用pop顯示方法時, * 直接通過名稱查詢對應的組件預設 * 將調用與事件配置解耦 * @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 控制彈窗實例的個數 * 2. 參數接收: open接收初始傳給pop的事件和參數配置, update 提供參數更新 * 3. 事件回調: options 配置屬性 { @[事件名稱]:事件回調 } 將作為事件綁定到pop上 * 4. 動態疊加: 內部將為組件配置 zIndex, 組件內需要自定義接收該參數,判斷如何處理層疊關系 * 5. 定位: 定位需要彈窗組件接收 position props 內部綁定樣式 * * @tips * 這里定位為了兼容 useMove做了接口調整,原接口直接輸出定位樣式。當前出position屬性, * 組件內需要自行處理定位樣式。 這里存在 style 合并和透傳的的問題, 通過透傳的style與 * props 內定義的style將分開處理, 即最終的結果時兩個style的集合, 且透傳的style優先級高于 * prop。所以如果直出定位樣式,通過透傳綁定給彈窗組件,后續的useMove拖拽樣式將始終被透傳樣式覆蓋 * * @api * - add(popId, options) 創建彈窗 * - update(popId, options) 更新彈窗配置(定位, props,events) * - remove(popId) 移除彈窗 * - replace(popId, options) 替換,如果多處調用同一彈窗,希望只顯示唯一同類彈窗時, * 使用該函數,多個彈窗公用相同的popId * - clearAllPop() 清空所有彈窗 * - updateIndex(popId) 更新彈窗層級 * - downIndex(popId) 層級下降一級 * - topIndex(popId) 層級置頂 * * @example01 - 一般使用 * * const [ * pops, * popTools * ] = usePop() * * * // 容器組件 * <component * v-for='(pop, popId) of pops' * :is='pop' * v-bind='pop.props' // 接收定位樣式 * v-on='pop.on' // 接收回調事件 * :key='popId'> * </component> * * // 調用彈窗 * popTools.add('popId', { * component: POP, // 彈窗組件 * position: { top: 200 } // 彈窗定位 * title: 'xxx', // 彈窗自定義props * @click(e){ // 彈窗事件 * .... * } * }) * * * @example02 - 預注冊 * 通過預注冊組件,再次調用時,只需要傳入對應注冊名稱,而不需要具體的配置項 * const [ pops, popTools ] = usePop() * * // 注冊本地彈窗 * popTools.componentsCache.add('userInfo', { * component: CMP, * opsition: { ... } * ... * }) * * // 調用 * 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 } } } /** * 更新組件參數 * @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 } } } /** * 多處調用同一pop時, 替換原顯示pop。 * @param popId * @param options */ function replace (popId, options) { remove(popId) add(popId, options) } function clearAllPop () { components.value = {} } /** * 向上一層級 * @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 }) } /** * 向下一層級 * @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 } ] } /** * 嵌套結構下的彈窗鉤子 */ // 容器鉤子 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, {}) }
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' /** * 注冊并返回彈窗快捷方法 * @param {*} popTools * @returns */ export function buildDefaultPopBind (popTools, componentsCache) { return (popId, component, options) => { componentsCache.add(popId, { component, // 默認定位 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) } } }
關于“vue usePop彈窗控制器如何實現”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。