题目重点:用可复用接口、受控状态与异步动作说明组件合同,补焦点和生命周期边界。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 原题仅给组件要求,本文教学采用modal dialog并提供open/title/body/actions/onClose/theme接口;原题若non-modal则焦点合同不同。
- 背景颜色配置指dialog或backdrop需要确认,训练分别提供surface/backdrop主题token;不允许任意不受控HTML字符串。
- 本文给组件设计与验收方案,没有嵌入未经浏览器验证的focus trap代码,不声称原题可运行实现或完整无障碍验收。
解题路线
- 一个open真值决定展示,关闭按钮、Escape、backdrop、action各自给close reason;调用onClose由父层决定是否关闭。不要同时维护内部open与外部open导致点击关闭后状态分裂。
- actions用稳定ID、label、handler、disabled和variant;确认动作可异步,pending时防重复提交,成功才按合同关闭,失败保留dialog并显示可读错误。关闭不自动保证已发请求撤销,需请求ID/AbortSignal或忽略过期结果。
- 标题与可读名称绑定,关闭用真实button并有accessible name;modal状态要求背景不可交互、焦点限于dialog,关闭后合理恢复到触发者。大块内容、危险操作或缺少可聚焦元素需要不同初始焦点策略;不是一律第一个button。
- native dialog可承载浏览器modal行为,但框架控制与showModal/close事件需同步;自定义portal需要处理stack、scroll、inert与卸载清理。多个dialog共享scroll lock必须计引用或由统一管理器,不能一关一个就解锁全部。
- 背景/主题值来自约定token,title/body用框架文本节点;尺寸、长标题、内容滚动、移动视口与zoom纳入布局验收。点击内容冒泡不能误当backdrop,Escape对IME组合和嵌套dialog需要事件合同。
- 测试计划覆盖controlled open、关闭reason、异步失败/双击、父层拒绝关闭、触发元素卸载、焦点恢复、tab循环、键盘关闭、scroll清理。真实浏览器/辅助技术验收与jsdom状态测试分开。WAI参考:https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/。
手动走一遍
训练“删除草稿”dialog:title、Cancel和Delete action。点Delete进入pending,第二次点击不再提交;请求失败保留内容和错误并回到可操作状态。成功后父层close,再把焦点移回草稿列表中合理位置,因为原触发草稿按钮可能已随删除消失。仅onClose一行不能覆盖这个生命周期。
规模与取舍
渲染随body节点/按钮数增长;重复挂载的事件与portal节点需清理,焦点候选扫描O(F)。大body耗时与布局成本不能用组件props数解释;异步延迟由业务请求决定。
易错点与追问
容易失分的边界
- 内外两个open真值。
- 关闭就宣称网络动作撤销。
- 自定义div按钮无键盘语义。
- 多个dialog各自无条件unlock。
- 把aria-modal属性当实际背景不可交互。
继续深挖
- 父层不同意关闭时如何保持状态一致?
- 危险确认默认焦点放哪里?
- 触发按钮被删除后focus恢复到哪?
- native dialog与portal怎样选择?
技术参考
- WAI Modal Dialog Pattern:公开概念/交互合同参考,教学示例不代表题目项目。
