网站首页弹窗管理

375 阅读2分钟

业务背景

网站首页会根据条件出一些活动、导流弹窗,这些弹窗有优先级,在高优先级的弹窗可以出的时候,出高优先级的弹窗,高优先级的弹窗不能出的时候才出下一个能出的低优先级弹窗。

难点

  • 各个弹窗能不能出的判断条件不同,有的需要前端控制,有的需要后端判断
  • 在高优弹窗不能出时才出低优弹窗

方案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请求,也能很方便的切换一次出一个还是依次出可以出的弹窗。