跳到主要内容

2 篇博文 含有标签「Gutenberg」

查看所有标签

WordPress FSE Block Validation Failed: JSON 引号缺失的隐蔽根因

· 阅读需 4 分钟

TL;DR

WordPress FSE 主题的 Pattern/Template 文件中,HTML 注释里的 JSON 属性如果某个字符串值缺少闭合引号 ",花括号数量仍然平衡,但 parse_blocks() 会静默将该块的 attrs 置为 null。Gutenberg 的 save 函数因此不输出 inline style,触发 Block validation failed。用 json.loads() 验证 JSON 合法性即可定位。

问题现象

WordPress Site Editor 打开 wishlist 模板时控制台报错:

Block validation: Block validation failed for `core/group`

Content generated by `save` function:
<div class="wp-block-group has-border-color has-neutral-200-border-color"></div>

Content retrieved from post body:
<div class="wp-block-group has-border-color has-neutral-200-border-color"
style="border-style:solid;border-width:1px;border-radius:var(--wp--custom--border--radius--lg);
padding-top:var(--wp--preset--spacing--40);...">

save 函数输出了正确的 CSS class,但完全丢失了 inline style,且内容为空。而文件中 HTML 明明包含 style 属性和子块。

根因

问题出在模板文件中 core/group 块的 HTML 注释 JSON 属性:

<!-- 原始(有误) -->
<!-- wp:group {"style":{"spacing":{"padding":{"top":"var(--wp--preset--spacing--40)",
"right":"var(--wp--preset--spacing--40)",
"bottom":"var(--wp--preset--spacing--40)",
"left":"var(--wp--preset--spacing--40)"}}, <!-- 此处缺少闭合引号 -->
"border":{"radius":"var(--wp--custom--border--radius--lg)","width":"1px","style":"solid"}},
"borderColor":"neutral-200","layout":{"type":"constrained"}} -->

注意 "left" 的值 "var(--wp--preset--spacing--40)" 缺少闭合引号 ",实际写成了 "var(--wp--preset--spacing--40)

为什么花括号检查发现不了:

正确:{ "left": "value" }  → 引号成对,花括号平衡
错误:{ "left": "value } → 引号不成对,但花括号仍然平衡

} 在 JSON 解析器眼中是字符串内容的一部分(因为引号没闭合),所以花括号计数不变。parse_blocks() 解析失败后不会报错,而是将该块的 attrs 直接置为 null

// parse_blocks 返回结果
[
'blockName' => 'core/group',
'attrs' => null, // 整个属性对象被丢弃
'innerHTML' => '<div ...>', // 原始 HTML 仍在
]

Gutenberg 拿到 null attrs 调用 save() 函数,自然不会输出任何 inline style,与文件中的 HTML 不匹配,触发 Block validation failed。

这个问题的隐蔽性在于:

  • 不会白屏,页面仍能渲染(使用 innerHTML 作为 fallback)
  • 花括号数量平衡,肉眼审查容易遗漏
  • Site Editor 中表现为"块需要恢复"的提示,容易被忽略
  • 审计脚本通常只检查花括号平衡和属性对应,不验证 JSON 合法性

解决方案

1. 定位问题

用 Python 验证 JSON 合法性:

python3 -c "
import json
with open('templates/wishlist.html') as f:
content = f.read()
# 提取 JSON 注释
marker = 'wp:group {'
start = content.index(marker) + len(marker) - 1
end = content.index(' -->', start)
json_str = content[start:end]
try:
json.loads(json_str)
print('JSON OK')
except json.JSONDecodeError as e:
print(f'Error at position {e.pos}: {e.msg}')
print(f'Context: ...{json_str[max(0,e.pos-20):e.pos+20]}...')
"

输出会精确定位错误:

Error at position 196: Expecting ',' delimiter
Context: ...g--40)}},"border":{"...

2. 修复 JSON

"left" 的值后面补上缺失的闭合引号:

<!-- 修复后 -->
<!-- wp:group {"style":{"spacing":{"padding":{"top":"var(--wp--preset--spacing--40)",
"right":"var(--wp--preset--spacing--40)",
"bottom":"var(--wp--preset--spacing--40)",
"left":"var(--wp--preset--spacing--40)"}}, <!-- 闭合引号已补上 -->
"border":{"radius":"var(--wp--custom--border--radius--lg)","width":"1px","style":"solid"}},
"borderColor":"neutral-200","layout":{"type":"constrained"}} -->

3. 验证修复

# 用 WP-CLI 验证 parse_blocks 正确解析
docker exec wp_cli wp eval '
$blocks = parse_blocks(file_get_contents(get_stylesheet_directory() . "/templates/wishlist.html"));
// 导航到目标块,检查 attrs 非 null
echo $blocks[...]["attrs"]["style"]["border"]["radius"];
' --allow-root

4. 预防措施

在 CI 中加入 JSON 注释合法性检查:

import json, re, sys

def check_block_json(filepath):
with open(filepath) as f:
content = f.read()
# 匹配所有 <!-- wp:xxx {...} --> 注释中的 JSON
for m in re.finditer(r'<!-- wp:\w+ (\{.*?\}) -->', content):
try:
json.loads(m.group(1))
except json.JSONDecodeError as e:
print(f"{filepath}: JSON error at comment position {m.start()}: {e}")
sys.exit(1)

check_block_json(sys.argv[1])

对类似需求感兴趣?联系合作

修复 Gutenberg Gradient Class 命名变更导致的 Block 验证失败

· 阅读需 2 分钟

在为客户开发 WordPress FSE 主题时遇到此问题,记录根因与解法。

TL;DR

Gutenberg 升级后,gradient 的 CSS class 命名规则从 has-{slug}-gradient 变为 has-{slug}-gradient-background。手写 Pattern HTML 中的旧 class 与 Gutenberg 验证逻辑不匹配,导致 Site Editor 报错。解决方案是批量替换 class 名称。

问题现象

所有历史 Pattern 在 Site Editor 中显示错误:

Block contains unexpected or invalid content
  • 新建 Pattern 无此问题
  • 前台渲染正常
  • 点击 "Attempt Recovery" 可恢复显示

根因分析

通过 DevTools Console 查看 Block validation failed 日志,对比 Expected 与 Actual:

Expected(Gutenberg 生成)

<div class="wp-block-group has-accent-gradient-background has-background">

Actual(Pattern 中手写)

<div class="wp-block-group has-accent-gradient has-background">

Gutenberg 版本升级后,gradient class 命名规则变更:

旧命名新命名
has-{slug}-gradienthas-{slug}-gradient-background

Block 验证时,Gutenberg 会根据块注释中的 JSON 属性(如 "gradient":"accent-gradient")重新计算期望的 HTML,与实际 HTML 比对。class 不匹配即报验证失败。

解决方案

1. 确认影响范围

# 查找使用旧 class 的文件
grep -r "has-[a-z0-9-]*-gradient " patterns/ --include="*.php"

2. 批量替换

# macOS/Linux 通用
sed -i '' 's/has-\([a-z0-9-]*\)-gradient /has-\1-gradient-background /g' patterns/*.php

# Linux (GNU sed)
sed -i 's/has-\([a-z0-9-]*\)-gradient /has-\1-gradient-background /g' patterns/*.php

注意

  • 只替换 HTML 标签中的 class 属性
  • 块注释中的 "gradient":"accent-gradient" 声明不需要改动
  • 正则末尾有空格,避免误匹配 has-accent-gradient-background

3. 验证修复

  1. 清理 WordPress 缓存:wp cache flush
  2. 刷新 Site Editor,确认错误消失
  3. 抽查几个 Pattern,验证前后台显示正常

预防措施

  1. 优先使用块注释属性:通过 JSON 属性(如 "gradient":"accent-gradient")设置样式,让 Gutenberg 自动生成 class,避免手写
  2. 关注 Gutenberg 更新日志:升级前检查 Breaking Changes
  3. 开发环境先验证:升级后在开发环境测试所有 Pattern,确认无验证错误再部署

对类似需求感兴趣?联系合作