业务背景
网站首页会根据条件出一些活动、导流弹窗,这些弹窗有优先级,在高优先级的弹窗可以出的时候,出高优先级的弹窗,高优先级的弹窗不能出的时候才出下一个能出的低优先级弹窗。
难点
- 各个弹窗能不能出的判断条件不同,有的需要前端控制,有的需要后端判断
- 在高优弹窗不能出时才出低优弹窗
方案1 -- 回调栈
思想
将低优先级的弹窗作为高优先级弹窗的Fallback,在弹窗能出时渲染弹窗的DOM元素,否则渲染它的Fallback。
弹窗的主处理函数
import React, {Children, cloneElement, isValidElement} from 'react'
const AllModal = () => {
return <ComponentStack>
<ComponentOne/>
<ComponentTwo/>
</ComponentStack>
}
const ComponentStack = ({children, fallback}) => {
return Children.toArray(children).reverse().reduce((prev, it) => {
if(isValidElement(it)){
return cloneElement(it, {fallback: prev})
}else{
return prev
}
}, fallback)
}
通用弹窗模版
const CommonModal = ({fallback}) => {
const [visible, setVisible] = useState(false)
useEffect(() => {
// 一些判断是不是能出的条件
}, [])
return visible ? <div></div> : <>{fallback}</>
}
缺点
- 并发判断所有请求能不能出,会出现低优先级弹窗闪现的问题
- 低优先级弹窗会出现重复渲染、重复请求后端接口的问题,有性能瓶颈
- 弹窗效果为依次出现弹窗,关掉高优的弹窗,马上出低优弹窗,体验不太好
方案二 -- Promise.all
思想
将每个组件能不能出的函数封装起来,并对外暴露,主函数在这些函数都返回后,判断出哪个弹窗,可以做到依次出、也可以做到一次只出一个。
通用弹窗模版
const CommonModal = () => {
const [visible, setVisible] = useState(false)
// 判断弹窗能不能出的条件
const shouldShow = async () => {
const res = await post()
if(res){
return true
}
return false
}
const slot = useMemo(() => {
return visible ? <div></div> : null
}, [visible])
}
弹窗主处理函数
const AllModal = () => {
const ModalList = [
ComponentOne(),
ComponentTwo(),
]
// 解析出弹窗的DOM元素
let slotList = ModalList.map(
(item) => {
return item.slot
}
)
// 解析出弹窗的能不能出的条件
const shouldShowResultList = useMemo(async () => {
const shouldShowList = ModalList.map(
(item) => {
return item.shouldShow()
}
)
const resultList = await Promise.all(shouldShowList)
return resultList
}, [])
// 找出能出且DOM元素不为null的弹窗
const showElementIndex = useMemo(() => {
return shouldShowResultList?.findIndex((item, index) => {
return item && slotList[index]
})
}, [shouldShowResultList])
if (!shouldShowResultList) return null
return showElementIndex != undefined && showElementIndex > -1 ? slotList[showElementIndex] : null
}
缺点
- 需要等所有弹窗判断能不能出的条件都返回后才出弹窗,会有延迟
总结
推荐使用第二种方案,可以减少判断弹窗能不能出的HTTP请求,也能很方便的切换一次出一个还是依次出可以出的弹窗。