title: "迁移区块以实现 iframe 编辑器兼容性" post_status: publish comment_status: open taxonomy: category: - gutenberg-docs post_tag: - Block Api Versions - Block Api - Reference Guides
迁移区块以实现 iframe 编辑器兼容性
概述
iframe 集成是现代化编辑体验持续努力的一部分。在模板编辑器原始 iframe 迁移的基础上,WordPress 正朝着在 iframe 内运行文章编辑器的方向发展。
本指南鼓励迁移区块至 API 版本 3,为文章编辑器计划的 iframe 集成做准备。它有助于提前验证区块在 iframe 编辑器中的工作状态,并协助更新区块以确保其在 iframe 环境中正常运行。
什么是 iframe 编辑器?
iframe 编辑器的优势
从技术角度来看,iframe 编辑器提供了几个重要优势:
- 样式隔离:管理后台样式不再影响编辑器内容,无需重置管理后台 CSS 规则。内容样式不再影响管理后台界面,因此区块和主题的 CSS 规则不再需要添加前缀。
- 视口相对单位:视口相对 CSS 单位(
vw、vh)能正确工作。编辑器内容的尺寸通常与管理后台页面尺寸不同,因此若无 iframe,像vw这样的单位将相对于管理后台页面计算。 - 媒体查询:媒体查询能原生工作,无需脆弱的变通方案。
- 更易开发:区块和主题开发者受益,因为前端样式几乎无需调整即可直接使用。这也适用于轻量级区块,其编辑器 DOM 结构与前端匹配。
- 选区处理:由于编辑器内容位于独立窗口,编辑器中的选区可以保持可见,同时编辑器 UI(例如 URL 输入字段)中也可以存在(折叠的)选区。
iframe 化的文章编辑器将通过减少样式冲突和提高布局准确性,使区块和主题开发者的工作更加轻松。
文章编辑器何时以 iframe 形式工作?
虽然包括模板编辑器在内的大多数编辑器已采用 iframe 模式,但为了向后兼容,当前文章编辑器仅在满足以下条件时才会以 iframe 形式工作(由 useShouldIframe 钩子 决定):
- 启用 Gutenberg 插件时: 当前主题为区块主题 或 所有已注册区块的
apiVersion均为 3 或更高版本 - 未启用 Gutenberg 插件时: 所有已注册区块的
apiVersion均为 3 或更高版本
简而言之,若您尚未在 iframe 编辑器中完成区块的全面测试,通过保持 apiVersion 为 2,可在多数情况下阻止文章编辑器以 iframe 形式运行。当确认您的区块能在 iframe 编辑器中正常工作后,即可迁移至 apiVersion 3。
文章编辑器何时会以 iframe 方式工作?
在 WordPress 7.0 中,无论已注册区块的 apiVersion 如何,文章编辑器都计划始终以 iframe 方式工作。
在此之前,为了鼓励开发者在 iframe 编辑器中测试,WordPress 6.9 引入了以下开发者警告和模式变更:
- 浏览器控制台警告:当区块以
apiVersion2 或更低版本注册时,WordPress 会在浏览器控制台中显示以下消息:Block with API version 2 or lower is deprecated since version 6.9. See: https://developer.wordpress.org/block-editor/reference-guides/block-api/block-api-versions/block-migration-for-iframe-editor-compatibility/ Note: The block "my-plugin/my-block" is registered with API version 2. This means that the post editor may work as a non-iframe editor. Since all editors are planned to work as iframes in the future, set the `apiVersion` field to 3 and test the block inside the iframe editor. - block.json 模式更新:block.json 模式 已更新,仅允许新创建或更新的区块使用
apiVersion: 3。旧版本(1或2)将不再通过模式验证。
如何在 iframe 文章编辑器中测试你的区块
所有核心区块已使用 apiVersion 3,因此只需将你的 apiVersion 更改为 3,即可让你的区块在 iframe 文章编辑器中工作。
但请确保不存在其他注册版本为 2 或更低的第三方区块。如果注册了版本 2 或更低的区块,文章编辑器可能无法作为 iframe 编辑器正常工作。
iframe 编辑器的技术考量
大多数区块无需修改即可在 iframe 编辑器中运行,但以下技术注意事项和需知事项已记录如下。
文档与窗口
iframe 将拥有与当前作为父窗口的管理页面不同的 document 和 window。编辑器脚本加载于管理页面中,因此通过访问 document 或 window 来操作内容将不再有效。
大多数使用 React 编写的区块应能继续正常工作,除非您依赖 document 或 window。修复方法是创建引用以访问相关文档 (ownerDocument) 或窗口 (defaultView)。无论是否使用 iframe,这都是良好的实践,可避免使用全局变量。
Using useRef
import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import { useRef, useEffect } from '@wordpress/element';
export default function Edit() {
const ref = useRef();
useEffect( () => {
const { ownerDocument } = ref.current;
const { defaultView } = ownerDocument;
defaultView.addEventListener( ... );
return () => {
defaultView.removeEventListener( ... );
};
}, [] );
const blockProps = useBlockProps( { ref } );
return (
<div { ...blockProps }>
Hello world!
</div>
);
}
使用 useRefEffect(推荐)
如果附加事件处理程序,请注意当 ref 发生变化时,useEffect 回调不会被调用。因此,建议使用新的 useRefEffect API,该 API 在 ref 发生变化时(除了传递的任何依赖项之外)会调用给定的回调函数。
import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import { useRefEffect } from '@wordpress/element';
export default function Edit() {
const ref = useRefEffect( ( element ) => {
const { ownerDocument } = element;
const { defaultView } = ownerDocument;
defaultView.addEventListener( ... );
return () => {
defaultView.removeEventListener( ... );
};
}, [] );
const blockProps = useBlockProps( { ref } );
return (
<div { ...blockProps }>
Hello world!
</div>
);
}
其他框架和库
对于编辑器,jQuery 等脚本会在父窗口(管理页面)中加载,这没有问题。当使用这些脚本与 iframe 中的区块交互时,您应该传递元素引用。
import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import { useRefEffect } from '@wordpress/element';
import jQuery from 'jquery';
export default function Edit() {
const ref = useRefEffect( ( element ) => {
jQuery( element ).masonry( … );
return () => {
jQuery( element ).masonry( 'destroy' );
}
}, [] );
const blockProps = useBlockProps( { ref } );
return (
<div { ...blockProps }>
Hello world!
</div>
);
}
如果库使用了全局的 window 或 document 且你无法控制怎么办?
向该库提交 issue 或 PR,建议其使用 ownerDocument 和 defaultView 替代全局对象。理想情况下,任何库都应允许以 iframe 内的元素作为目标进行初始化。这并非不可能。欢迎随时联系我们提及此问题。
在此期间,你可以使用在 iframe 内加载的脚本。我们已经将所有前端脚本加载到 iframe 中以解决此类情况,但请注意,理想情况下你根本不应使用在 iframe 中加载的脚本。你可以使用 defaultView 来访问脚本。
import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import { useRefEffect } from '@wordpress/element';
import jQuery from 'jquery';
export default function Edit() {
const ref = useRefEffect( ( element ) => {
const { ownerDocument } = element;
const { defaultView } = ownerDocument;
// 使用在 iframe 中加载的脚本。
// 脚本是异步加载的,因此检查脚本是否已加载。
// 依赖项加载完成后,区块将重新渲染。
if ( ! defaultView.jQuery ) {
return;
}
defaultView.jQuery( element ).masonry( … );
return () => {
defaultView.jQuery( element ).masonry( 'destroy' );
}
} );
const blockProps = useBlockProps( { ref } );
return (
<div { ...blockProps }>
Hello world!
</div>
);
}