标准答案
- 浮层通常用 Portal 渲染到统一容器,避免被父级样式裁剪或层叠上下文影响。
- 组件状态要支持受控和非受控,常见 props 包括 open、defaultOpen、onOpenChange。
- 弹窗类组件要处理焦点进入、焦点陷阱、ESC 关闭、点击遮罩关闭、关闭后焦点回到触发元素。
- 页面滚动锁定、z-index 管理、嵌套弹窗、动画卸载时机都要有统一策略。
- 可访问性上要设置 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 和滚动锁定,嵌套场景互相冲突。