防御性数据设计(Defensive data design)是一套针对 WordPress Gutenberg 项目的开发规范,主题是:为可能丢失用户工作或让用户陷入困境的代码提供安全默认值,并把它们当作可执行的检查,而不是停留在意图层面。
一、用户可见的错误
错误提示要说明原因,而不只是症状。当失败本身带有可用消息时,应展示该消息(必要时连同其代码);没有可用消息时再回退到自己的文案。一个捕获到错误却只显示固定字符串的 catch 属于缺陷。
来自 apiFetch 的拒绝形状各不相同,在从中读取原因之前,请先查看根目录 AGENTS.md 中的相关陷阱说明。
REST 校验失败会在 error.data.params 中携带逐字段的原因,顶层 message 只给出字段名。
如果消息包含 HTML,应使用 @wordpress/dom 的 __unstableStripHTML 去除标签,而不是直接丢弃整条消息。
绝不吞掉错误。空 catch 只有在失败是预期之内且回退正确时才可接受,并且要用注释说明是哪种失败。
展示足够多的失败信息以便用户采取行动,在空间允许时让内容可复制。不要把用户内容放进载荷,也不要放入当前 URL,因为它可能携带 nonce。
编写字符串前先阅读文案指南中的“错误消息”部分:用平实的词,点明原因,给出下一步。
二、变更与破坏性操作
优先选择可逆的变更,并让变更本身与回退路径都可见。不可逆操作应当更难被触发。
在执行有后果的变更之前,展示它作用于什么、谁将看到结果,以及它留下的状态。默认使用草稿、私有或可逆;把公开或永久设为需要刻意选择的选项。
把内容移入回收站应当容易,删除则应当困难。如果两者位于同一菜单、共用同一个确认框,那么这个确认框没有起到任何作用。
确认对话框要回显受影响的对象:条目的标题,或批量操作的数量。
不可逆的确认要在确认按钮上使用 isDestructive,并在标签中说明(写“永久删除”,而不是“删除”)。遵循破坏性操作模式。
变更成功要发出通知,并在先前值仍在作用域内时提供撤销。应显式捕获该值,而不是弹出撤销栈,因为后者会一并回退无关的编辑。
Snackbar 只渲染一个操作;更多操作会记录警告并截断为 actions[0](packages/components/src/snackbar/index.tsx)。撤销或另一个按钮,二选一,不能同时。
Snackbar 在出现六秒后自动消失,因此对于用户必须读完的内容,请使用 explicitDismiss 或改用其他通知。文案指南说明了文本本身可以有多长。
变更失败时,要让用户能够重试:保留其输入、恢复一致状态、释放任何仍处于忙碌状态的控件,并确保重试是安全的。
saveEntityRecord 和 deleteEntityRecord 不会向撤销栈添加任何内容,因此任何已经持久化的内容都需要自己的恢复路径。editEntityRecord 会记录一个撤销层级,除非以 undoIgnore 调用。
三、可读性
充分利用可用空间:直接展示值本身,而不是用一个把值压平的标签,避免让人打开面板才能看到。
确保标签反映真实情况,包括它本来会隐藏的限定条件。例如:
受密码保护的文章上显示“已发布”,会被读成可公开阅读,而事实并非如此。
模板带有属于站点的本地编辑时,却读起来像是主题自带的。
四、非你创建的数据
在不可信数据进入的地方设防——REST 响应、文章元数据、theme.json、被过滤的编辑器设置、存储读取——而不是在下游的每一次读取处设防。区块属性只被部分覆盖:packages/blocks/src/api/parser/get-block-attributes.ts 会在解析值未通过类型检查时替换为声明的默认值,但 blocks.getBlockAttributes 过滤器在该检查之后运行,而 updateBlockAttributes 完全不检查,因此类型错误的值仍会到达保存函数和 PHP。
边界处的 JSON.parse 要放在 try/catch 中,并在映射或迭代之前对结果做形状检查(Array.isArray、is_array):形状错误但语法合法的 JSON 才是常见情况,而不是格式错误的 JSON。
在 PHP 中,失败模式取决于你触碰的内容,而渲染期间的致命错误会让每位访客看到白屏:
警告并返回 null:对不可迭代对象做 foreach;对 null、int、bool 或 float 取偏移量。坏数据会悄悄继续流动。
致命错误:对字符串取字符串偏移($str['slug']);把数组或对象当作偏移量;把对象当作数组使用。应优先防护这些。
检查 throw 落在哪里。React 错误边界只捕获渲染阶段的错误——在 registry.subscribe 回调、异步点击处理器或 promise 链中抛出的错误会逃逸出去,并可能悄悄丢弃用户的编辑。