标准答案

  1. 浮层通常用 Portal 渲染到统一容器,避免被父级样式裁剪或层叠上下文影响。
  2. 组件状态要支持受控和非受控,常见 props 包括 open、defaultOpen、onOpenChange。
  3. 弹窗类组件要处理焦点进入、焦点陷阱、ESC 关闭、点击遮罩关闭、关闭后焦点回到触发元素。
  4. 页面滚动锁定、z-index 管理、嵌套弹窗、动画卸载时机都要有统一策略。
  5. 可访问性上要设置 role、aria-modal、aria-labelledby、aria-describedby,并保证键盘可操作。

题目解析

这道题考的是组件设计完整度。Portal 只是第一步,交互和可访问性才是浮层组件的难点。

受控和非受控设计会影响组件库易用性。业务弹窗通常由父组件控制 open,轻量提示组件可以内部控制。

抽屉、弹窗、Popover 的关闭策略不同,不能所有浮层都只用点击外部关闭。

代码示例

下面展示受控弹窗 API 的基本形态。

TSX
function Dialog({
  open,
  onOpenChange,
  title,
  children
}: {
  open: boolean
  onOpenChange: (open: boolean) => void
  title: string
  children: ReactNode
}) {
  if (!open) {
    return null
  }

  return createPortal(
    <div role="dialog" aria-modal="true" aria-label={title}>
      <button onClick={() => onOpenChange(false)}>关闭</button>
      {children}
    </div>,
    document.body
  )
}

常见误区

  • 弹窗只支持内部状态,业务无法从外部控制打开和关闭。
  • 关闭弹窗后没有恢复焦点,键盘用户丢失当前位置。
  • 多个浮层各自管理 z-index 和滚动锁定,嵌套场景互相冲突。

作者信息