vue usePop彈窗控制器的實(shí)現(xiàn)
當(dāng)UI庫(kù)彈窗無(wú)法滿(mǎn)足自定義需求時(shí),需要我們自己開(kāi)發(fā)簡(jiǎn)單的彈窗組件。彈窗組件與普通業(yè)務(wù)組件開(kāi)發(fā)沒(méi)有太大區(qū)別,重點(diǎn)在多彈窗之間的關(guān)系控制。例如: 彈窗1,彈窗2 由于觸發(fā)時(shí)機(jī)不同,需要不同的層疊關(guān)系,后觸發(fā)的始終在最前端,點(diǎn)擊彈窗頭改變層疊關(guān)系。 單一彈窗多處調(diào)用等。這里封裝基礎(chǔ)的管理鉤子,簡(jiǎn)化這些問(wèn)題的處理。
功能目標(biāo)
- 單例,多例彈窗
- 可配置彈窗自定義參數(shù)
- 可接收彈窗自定義事件
- 層級(jí)控制
- 自定義定位
該鉤子的目的主要為了處理彈窗之間的控制關(guān)系,具體如何渲染交由調(diào)用方
快速使用
// 主容器
import { usePopContainer, buildDefaultPopBind, position } from '@/hooks/usePop'
import UserInfoPop form './UserInfoPop.vue'
// 快捷工具,將內(nèi)部鉤子通過(guò)依賴(là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': () => { ... } //彈窗事件
})
所有彈窗都通過(guò)popId,查找or判斷唯一性。
配置參數(shù):以
@開(kāi)頭的都將組為組件的事件被綁定, 除了@[事件名]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í)就是
removeadd的包裝方法
更新 update
const [popMap, popTools] = usePopContainer()
popTools.replace(popId, {
component: UserPop, // 彈窗組件
useId: 'xxx', // 彈窗Props
'@close': () => { ... } //彈窗事件
})
// 更新參數(shù)
popTools.update(popId, {
useId: 'yyy'
})
通過(guò)popId 查詢(xún)彈窗,將新傳入的參數(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查詢(xún)預(yù)注冊(cè)配置
除了局部緩存, componentsCache, 模塊還導(dǎo)出了 globalComponentsCache 全局公共緩存。
依賴(lài)注入
為了方便父子組件調(diào)用,提供了usePopContainer usePopChildren 方法,
// 父組件
const [popMap, popTools] = usePopContainer()
// 子組件
const { popTools } = usePopChildren()
popTools.add({
...
})
函數(shù)接收依賴(lài)注入標(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)用同一彈窗,希望只顯示唯一同類(lèi)彈窗時(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í),
* 直接通過(guò)名稱(chēng)查詢(xún)對(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. 單例, 多例: 通過(guò)不同的 popId 控制彈窗實(shí)例的個(gè)數(shù)
* 2. 參數(shù)接收: open接收初始傳給pop的事件和參數(shù)配置, update 提供參數(shù)更新
* 3. 事件回調(diào): options 配置屬性 { @[事件名稱(chēng)]:事件回調(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 合并和透?jìng)鞯牡膯?wèn)題, 通過(guò)透?jìng)鞯膕tyle與
* props 內(nèi)定義的style將分開(kāi)處理, 即最終的結(jié)果時(shí)兩個(gè)style的集合, 且透?jìng)鞯膕tyle優(yōu)先級(jí)高于
* prop。所以如果直出定位樣式,通過(guò)透?jìng)鹘壎ńo彈窗組件,后續(xù)的useMove拖拽樣式將始終被透?jìng)鳂邮礁采w
*
* @api
* - add(popId, options) 創(chuàng)建彈窗
* - update(popId, options) 更新彈窗配置(定位, props,events)
* - remove(popId) 移除彈窗
* - replace(popId, options) 替換,如果多處調(diào)用同一彈窗,希望只顯示唯一同類(lèi)彈窗時(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è)
* 通過(guò)預(yù)注冊(cè)組件,再次調(diào)用時(shí),只需要傳入對(duì)應(yīng)注冊(cè)名稱(chēng),而不需要具體的配置項(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.js彈窗組件的知識(shí)點(diǎn)總結(jié)
- vue實(shí)現(xiàn)點(diǎn)擊按鈕“查看詳情”彈窗展示詳情列表操作
- vue實(shí)現(xiàn)彈窗引用另一個(gè)頁(yè)面窗口
- 很棒的vue彈窗組件
- Vue彈窗的兩種實(shí)現(xiàn)方式實(shí)例詳解
- VUE實(shí)現(xiàn)可隨意拖動(dòng)的彈窗組件
- vue 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的全局調(diào)用彈窗案例
- vue+elementui通用彈窗的實(shí)現(xiàn)(新增+編輯)
- vue打開(kāi)子組件彈窗都刷新功能的實(shí)現(xiàn)
相關(guān)文章
mini-vue渲染的簡(jiǎn)易實(shí)現(xiàn)
本文主要介紹了mini-vue渲染的簡(jiǎn)易實(shí)現(xiàn),主要簡(jiǎn)單來(lái)實(shí)現(xiàn)一個(gè)虛擬dom渲染真實(shí)dom,以及更新的方法。具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
VUE中v-on:click事件中獲取當(dāng)前dom元素的代碼
這篇文章主要介紹了VUE中v-on:click事件中獲取當(dāng)前dom元素的代碼,文中同時(shí)給大家提到了v-on:click獲取當(dāng)前事件對(duì)象元素的方法,需要的朋友可以參考下2018-08-08
Pure admin-Router標(biāo)簽頁(yè)配置與頁(yè)面持久化實(shí)現(xiàn)方法詳解
這篇文章主要介紹了Pure admin-Router標(biāo)簽頁(yè)配置與頁(yè)面持久化實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2023-01-01
解決VUEX刷新的時(shí)候出現(xiàn)數(shù)據(jù)消失
這篇文章主要介紹了解決VUEX刷新的時(shí)候出現(xiàn)數(shù)據(jù)消失,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
Ant Design的可編輯Tree的實(shí)現(xiàn)操作
這篇文章主要介紹了Ant Design的可編輯Tree的實(shí)現(xiàn)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10
Vue實(shí)現(xiàn)項(xiàng)目部署到非根目錄及解決刷新頁(yè)面時(shí)找不到資源
這篇文章主要介紹了Vue實(shí)現(xiàn)項(xiàng)目部署到非根目錄及解決刷新頁(yè)面時(shí)找不到資源問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03

