跳到正文
原文
Gutenberg Times(区块编辑器)· Ryan Welcher·· 2026-07-18精选编辑评分87

WordPress 7.1 强制文章编辑器使用 iframe、区块开发者如何迁移

The post editor is going full iframe: what block developers need to know before WordPress 7.1

编辑导读

WordPress 7.1 将对所有主题强制使用 iframe 文章编辑器画布,不再依据区块的 apiVersion 或主题类型决定。

推荐理由

文章梳理了 WordPress 7.1 强制 iframe 的时间线,并提供 JavaScript、CSS 与第三方库迁移及测试方法。

译文尚不完整,完整内容请切换到原文。

For years, the post editor has lived a double life. The Site Editor renders your blocks inside an iframe. The post editor — where most people actually spend their time — renders them directly in the admin page. That split ends with WordPress 7.1: the post editor canvas will always be an iframe, on every theme, no matter what apiVersion your blocks declare. The Gutenberg plugin has been enforcing exactly this for months. If you ship blocks, assume the iframe.

如果你的区块从不涉及全局 document 或 window,那么把 "apiVersion": 2 改为 "apiVersion": 3(位于 block.json 中)后,你大概就不用再往下看了。至于其他人——尤其是任何发布封装第三方库的区块的人——iframe 会改变代码运行的位置与标记存放的位置。问题就出在这个差距上。

快速参考指南:你的区块准备好了吗?

An infographic showing the checks and fixes for readying custom blocks for the WordPress 7.1 iframed editor

时间线一览表

版本会发生什么
June 21, 2021iframe 编辑器在 make.wordpress.org 上发布
WordPress 6.9(Dec 2025)当区块以 apiVersion 2 或更低版本注册时,会在控制台发出警告(附带 SCRIPT_DEBUG)。block.json 架构现在仅验证 apiVersion: 3。
WordPress 7.0(Apr 2026)iframe 决策现在会查看实际插入文章中的区块,而不是每个已注册的区块。所有插入的区块均为 v3+ → 画布使用 iframe。插入单个 v1/v2 区块 → 即时移除 iframe。目前尚未强制执行。
Gutenberg 22.6+iframe 无论主题如何都会被强制启用——这是收集反馈的阶段。
WordPress 7.1(Aug 19, 2026)无论 apiVersion 如何,iframe 都会在所有主题上强制启用。这些条件已被取消,而不是收紧。

WordPress 7.0 的变化虽然微妙,却很重要:在 7.0 之前,只要任何已启用的插件注册了一个 apiVersion: 2 区块——即使该区块从未在文章中使用——整个编辑器就会对所有人禁用 iframe。现在只有实际插入的区块才会计入。你的 v3 区块会使用 iframe,直到用户插入一个旧版区块,此时编辑器会在不使用 iframe 的情况下悄然重新加载画布。配套插件提供了一个 legacy-api-v2 区块,让你能够观察这一过程——将它插入一篇其他区块均为 v3 的文章中,iframe 就会消失。在 7.1 中,这扇逃生门将被关闭。

顺带一提,值得知道的是:“所有主题”这一决定是在 WordPress 7.1 Beta 1 中落地的,并且正有意公开测试。Gutenberg 于 2026 年 7 月 10 日合并了“Post editor: always iframe”(#74042),彻底删除了主题和 apiVersion 条件。7.1 版本负责人批准此次合并的前提是,如果 Beta 1 的反馈暴露出实际问题,团队可以“转向更温和的方案”——所谓更温和的方案,是指仅对区块主题强制执行,而其他所有主题继续遵循 7.0 的规则。目前没有承诺任何具体机制;计划是根据测试版实际发现的问题作出响应。

这也是加大测试力度的理由,而不是坐等结果。如果发生回滚,使用 iframe 和不使用 iframe 的编辑器都将在现实环境中存在更长时间——而无论最终走向如何,你的区块都必须兼容两者。

还值得注意的是,那些会因 7.1 的变更而出问题的区块,很可能已经在站点编辑器中出问题。

为什么 iframe 是件好事

这不是为了改变而改变。在 iframe 中呈现画布,为编辑器提供了一个真正的文档边界:

  • 后台 CSS 不再泄漏到你的内容中。 不再有相邻的 #wpadminbar 样式重置,也不再有后台样式在不知不觉中改变区块在编辑器与前端的呈现方式。
  • 视口单位和媒体查询终于可以正常工作了。 vw、vh 和 @media 规则会根据画布而不是后台页面进行解析——因此平板电脑/移动设备预览和缩小视图的行为会真正与前端一致。
  • 现在所见的结果更接近最终获得的效果。 画布文档基于主题的样式构建,而不是基于后台的样式。

这给区块开发者带来了什么问题?编辑器 JavaScript 在后台页面中运行,但区块的 DOM 位于另一个文档中。每一个固化在 document.querySelector(...) 和 window.addEventListener(...) 中的假设现在都变得错误了。

实际会出现什么问题(以及如何修复)

以下所有内容都可以通过配套插件进行演示——每种模式都提供了一组损坏/修复后的区块:GitHub 上的 iframe-editor-examples。

1. 全局 window 和 document 引用

经典问题:读取视口或监听 resize 事件的区块。

JavaScript

// ❌ Broken in the iframed editor
useEffect( () => {
	const update = () => setWidth( window.innerWidth );
	update();
	window.addEventListener( 'resize', update );
	return () => window.removeEventListener( 'resize', update );
}, [] );

编辑器脚本加载在后台页面中,因此 window 是后台窗口。在使用 iframe 的编辑器中,它会报告错误的宽度,并且不会对画布调整大小作出反应——切换到平板电脑预览时,数值也不会变化。

修复方法是根据区块自身的 DOM 元素获取 document 和 window:

JavaScript

// ✅ Fixed — works iframed or not
import { useRefEffect } from '@wordpress/compose';

const ref = useRefEffect( ( element ) => {
	const { defaultView } = element.ownerDocument;
	const update = () => setWidth( defaultView.innerWidth );
	update();
	defaultView.addEventListener( 'resize', update );
	return () => defaultView.removeEventListener( 'resize', update );
}, [] );

const blockProps = useBlockProps( { ref } );

需要注意两点:

  • element.ownerDocument 是区块呈现所在的文档——使用 iframe 时是 iframe 的文档,不使用时则是后台文档。ownerDocument.defaultView 是该文档的 window。以这种方式编写的代码是与上下文无关的:无论是否存在 iframe,它都能正常工作。
  • 使用 useRefEffect(来自 @wordpress/compose),而不是 useRef + useEffect:当 ref 发生变化时,它会重新运行回调,因此如果区块在文档之间发生移动,监听器就会重新绑定到正确的 window。

2. “点击外部时关闭”及其他文档级事件

我最喜欢这个问题,因为它的失败方式很奇怪。一个在点击外部时关闭的下拉菜单,其实现方式与所有 React 教程所教的一样:

JavaScript

// ❌ Broken in the iframed editor
useEffect( () => {
	const closeOnOutsideClick = ( event ) => {
		if ( ! containerRef.current.contains( event.target ) ) {
			setIsOpen( false );
		}
	};
	document.addEventListener( 'click', closeOnOutsideClick );
	return () => document.removeEventListener( 'click', closeOnOutsideClick );
}, [] );

在使用 iframe 的编辑器中,画布内部的点击发生在 iframe 的文档中。它们不会冒泡到后台文档,因此监听器永远不会触发。结果是:点击画布中的另一个区块时,下拉菜单仍然保持打开;但点击后台侧边栏时,它却会关闭。相同的代码、相同的区块,在不使用 iframe 的编辑器中却能完美工作。用户会提交这种“无法复现”的错误报告——因为进行测试的人碰巧在文章中放置了一个 v2 区块,它会悄悄移除 iframe,让一切恢复正常。

修复:原则相同,将监听器绑定到 element.ownerDocument 而不是 document(完整的 useRefEffect 版本请参阅插件)。

3. 编辑器样式被加载到错误的文档中

如果你使用 enqueue_block_editor_assets 设置区块的编辑器样式,这些样式会加载到后台页面中——位于 iframe 之外。一旦画布使用 iframe,它们就会悄悄停止生效:

PHP

// ❌ Loads in the admin page — never reaches the iframed canvas.
function myplugin_enqueue_editor_styles() {
	wp_enqueue_style( 'myplugin-editor', plugins_url( 'editor.css', __FILE__ ) );
}
add_action( 'enqueue_block_editor_assets', 'myplugin_enqueue_editor_styles' );

修复方法是通过 block.json 注册编辑器样式,无论是否使用 iframe,WordPress 都会将其注入画布文档:

JSON

{
	"editorStyle": "file:./index.css"
}

(如果需要主题级别的编辑器样式,add_editor_style() 也会被复制到 iframe 中。)

演示插件会呈现这样的效果:同一个区块同时带有来自 editorStyle 的绿色横幅和来自 enqueue_block_editor_assets 的红色横幅。数一下横幅——两个表示没有使用 iframe,一个则表示你处于 iframe 中。

4. 为旧版编辑器编写的过时 CSS

上一节讲的是 CSS 被加载到错误的文档中。这一节则是更隐蔽的相反情况:样式表被加载到正确的文档中——直接注入画布,正如预期——但结果仍然错误,因为这些样式原本是用来描述某种内容的。以下规则会在画布成为独立文档后悄然停止匹配,或开始过度匹配。这些代码多年来一直存在于主题和插件中,一直“正常运作”,直到 iframe 被强制启用。

以管理员 body 类名为依据的选择器

最常见的一种,其失败方式与“点击外部时关闭”缺陷完全相同——悄无声息。

CSS

/* ❌ The canvas body no longer carries these classes */
.wp-admin .my-block { padding: 2rem; }
body.block-editor-page .my-block__title { font-size: 2rem; }

在 iframe 内,画布 <body> 是一个干净的文档——没有 wp-admin,也没有 block-editor-page。选择器匹配不到任何内容,编辑器样式便完全消失。同样的区块、同样的样式表,在非 iframe 编辑器中则运行得完美无缺。

CSS

/* ✅ Scope to the block, not the admin chrome */
.my-block { padding: 2rem; }
.my-block__title { font-size: 2rem; }

.editor-styles-wrapper 在 iframe 内仍然会包裹画布内容,因此如果你确实需要仅适用于编辑器的样式,.editor-styles-wrapper .my-block 仍可继续使用——但管理员祖先选择器原本几乎就没有必要。

用于补偿管理员界面框架的偏移量

CSS

/* ❌ Subtracting the admin sidebar and adminbar from the viewport */
.my-fullwidth { width: calc( 100vw - 160px ); } /* 160px = admin menu */
.my-toolbar   { position: fixed; top: 32px; }   /* 32px = #wpadminbar */

这是前面那个优势的另一面:如今 100vw 针对的是画布而非管理员页面,因此就没有需要减去的侧边栏——于是 calc() 偏移过多,而 top: 32px 会将工具栏推到管理员栏下方,尽管该文档中并不存在管理员栏。

CSS

/* ✅ The canvas is the viewport now — no compensation needed */
.my-fullwidth { width: 100vw; }
.my-toolbar   { position: fixed; top: 0; }

为防止样式渗漏而构建的特异性屏障

CSS

/* ❌ Cranked up to beat leaking admin styles */
.editor-styles-wrapper .my-block p {
	font-family: Georgia, serif !important;
	line-height: 1.6 !important;
	box-sizing: border-box !important;
}

iframe 已经阻止了管理员 CSS 渗入——这正是使用 iframe 的好处之一。这些 !important 和重置规则已无管理员样式需要覆盖,但现在它们确实会覆盖 iframe 加载到画布中的主题样式。结果是:编辑器预览会逐渐偏离前端——这与使用 iframe 的目的恰恰相反。

CSS

/* ✅ Let theme styles through; set only what your block truly owns */
.my-block p { font-family: Georgia, serif; }

有两点需要注意:

  • 其模式与 JavaScript 修复方案相同:不要再描述管理员界面,而要开始描述你的区块。任何提及 .wp-admin、#wpadminbar 或 .block-editor-page 的选择器,都在试图匹配画布文档中已不存在的界面框架。
  • 其中大多数原本都是针对 iframe 所能解决问题的变通方案。删除它们通常就是修复方法。

5. 假定只有一个全局上下文的第三方库

这是实际应用中最大的隐患。瀑布流布局、滑块、灯箱、地图——整整一代库在编写时都假定只有一个 document:

JavaScript

// Inside some-legacy-lib.js
const targets = document.querySelectorAll( selector ); // finds nothing in the iframe

你的区块调用该库,库查询管理员文档,发现零个匹配项,于是悄无声息地什么也不做。没有错误,也没有警告——区块只是停止获得增强功能。

可选方案按优先顺序排列如下:

  • 传递元素,而不是选择器。如果该库接受元素(lib.init( element )),把区块在 useRefEffect 中的元素传给它,通常就没问题。
  • 修补这个库。 对于无人维护的依赖,patch-package 是务实的解决方案:编辑 node_modules 中的模块,以解决从元素(node.ownerDocument)触发的 document/window,运行 npx patch-package <pkg>,提交补丁,并添加 postinstall 脚本。官方迁移指南会逐步演示如何为 @panzoom/panzoom 实际打补丁。
  • 设防并退出。 如果该库加载在 iframe 内部(前端脚本就是如此),请在使用之前检查它是否存在于 defaultView 上:if ( ! defaultView.jQuery ) return;

那么 apiVersion: 3 实际上会做什么?

比你可能想象的少——而这正是重点。在 block.json 中声明 "apiVersion": 3 并不会改变你的区块的渲染方式;它是一个表明你的区块已为 iframe 做准备的信号。自 WordPress 6.3 起,所有核心区块都已使用 v3。对大多数区块而言,迁移实际上就是改一行……然后才 是真正的工作:测试你的 edit 组件(或它引入的库)没有触碰全局的 document/window。

关于 7.1 还要明确一点:无论 apiVersion 如何,iframe 都会强制启用。继续使用 v2 不再意味着你可以选择退出——它只意味着你同时会看到控制台警告以及故障。

如何立即测试

你不需要等到 7.1。你要测试的是你的区块在两种状态下都能正常工作——在 iframe 中和不在 iframe 中——因为这两种状态在现实环境中还会并存一段时间。

在 iframe 中: 安装 Gutenberg 插件 22.6+。无论使用何种主题,它都会强制启用 iframe,因此这是提前适应未来的最快方式。7.1 Beta 1 也是如此——我已确认它会在经典主题上强制启用 iframe,这正是 8 月即将发布的行为。

不在 iframe 中: 运行不安装插件的 WordPress 7.0,并在你的区块旁插入一个 v1/v2 区块——画布会动态移除 iframe。配套插件的 legacy-api-v2 区块正是为此而存在。任何主题都可以:核心 7.0 在决定是否使用 iframe 时完全不检查主题,因此你无需费心寻找经典主题来复现这种情况。

确认你当前所处的状态: element.ownerDocument !== document,或在开发者工具中查找 iframe[name="editor-canvas"]。

站点编辑器多年来一直使用 iframe——如果你的区块在那里已经能正常工作,你就基本完成了迁移。

配套插件包含 wp-env 设置、一个添加 Gutenberg 以启用强制模式的示例覆盖文件(将其复制到 .wp-env.override.json),以及两个 Playground 蓝图——每种状态一个,因此你可以在两个标签页之间切换 iframe 状态,无需安装任何东西。

区块作者检查清单

  1. 在每个 block.json 中设置 "apiVersion": 3。
  2. 检查你的编辑器代码中是否存在 window. 和 document.——每处命中都值得怀疑。通过 useRefEffect 替换为 element.ownerDocument / .defaultView。
  3. 检查是否存在 enqueue_block_editor_assets——将影响画布的样式移到 block.json 中的 editorStyle。
  4. 检查你的编辑器 CSS 中是否存在 .wp-admin、#wpadminbar 和 .block-editor-page,管理界面外壳偏移以及 !important
  5. 审计第三方库:传入元素而非选择器,必要时打补丁。
  6. 测试两种状态,而不是两种主题:在 iframe 中(启用 Gutenberg 22.6+)和不在 iframe 中(不安装插件,插入 v1/v2 区块)。
  7. 在启用 SCRIPT_DEBUG 的情况下观察控制台——弃用警告会告诉你哪些已注册区块仍在使用 v1/v2。

注意清单上没有什么:检查当前活动主题是否为区块主题。Core 通过两种方式公开这个答案——编辑器设置中的 __unstableIsBlockBasedTheme,以及 REST themes 端点上的 is_block_theme——其背后还有一个暂缓的 PR 和一场正在进行的讨论。你的区块永远不需要询问。element.ownerDocument !== document 回答了唯一会影响你的问题版本。如果你好奇该主题检查在底层是如何工作的,那是另一个话题:WordPress 如何判定主题是“区块主题”。

资源

  • WordPress 7.0 中的 iframe 编辑器变更——这一切所基于的开发者说明
  • 文章编辑器:始终使用 iframe(#74042)——使 7.1 始终采用 iframe 的已合并 PR,以及 beta 反馈计划背后的讨论
  • 7.1 路线图——最初的区块主题优先计划,后被 #74042 取代
  • 为完整的 iframe 集成准备文章编辑器——6.9 的基础工作
  • 为 iframe 编辑器兼容性迁移区块——官方迁移指南
  • 区块 API 版本——每个版本的含义
  • 加载编辑器资源
  • 主题的编辑器样式
  • 配套演示插件:iframe-editor-examples

来源:Gutenberg Times(区块编辑器) · https://gutenbergtimes.com/the-post-editor-is-going-full-iframe-what-block-developers-need-to-know-before-wordpress-7-1/

部分链接为推广/关联链接。