本文档介绍了 WordPress 编辑器向 iframe 集成迁移的背景和必要性,旨在帮助开发者将区块迁移至 API 版本 3,以确保在 iframe 编辑器中的兼容性。它涵盖了 iframe 编辑器的技术优势、启用条件、测试方法以及迁移时的技术注意事项。
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>
);
}The iframe integration is part of an ongoing effort to modernize the editing experience. WordPress is moving toward running the post editor inside an iframe, building upon the original iframe migration in the template editor.
This guide encourages migration of blocks to API version 3 in preparation for the planned iframe integration of the post editor. It helps verify in advance that blocks work in the iframe editor and assists in updating blocks so they work correctly in the iframe environment.
From a technical perspective, the iframe editor provides several important benefits:
vw, vh) work correctly. The dimensions of the editor content are usually not the same as the dimensions of the admin page, so without an iframe, units like vw would be relative to the admin page.The iframed post editor will make life easier for block and theme authors by reducing styling conflicts and improving layout accuracy.
While most editors, including the template editor, already work as iframes, for backward compatibility, the current post editor only works as an iframe when the following conditions are met (determined by the useShouldIframe hook):
apiVersion 3 or higherapiVersion 3 or higherIn summary, if you haven’t been able to fully test your blocks in the iframe editor yet, by maintaining apiVersion 2, you can prevent the post editor from working as an iframe in most cases. Once you’ve confirmed that your blocks work in the iframe editor, you can then migrate to apiVersion 3.
In WordPress 7.0, the post editor is planned to always work as an iframe, regardless of the apiVersion of registered blocks.
Ahead of this, to encourage developers to test in the iframe editor, WordPress 6.9 introduces the following developer warnings and schema changes:
apiVersion 2 or lower, WordPress displays the following message in the browser console: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/<br />
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.apiVersion: 3 for new or updated blocks. Older versions (1 or 2) will no longer pass schema validation.All core blocks are already using apiVersion 3, so simply changing your apiVersion to 3 should allow your blocks to work in the iframe post editor.
However, make sure that no other third-party blocks registered with version 2 or lower are present. If blocks with version 2 or lower are registered, the post editor may not work as an iframe editor.
Most blocks should work in the iframe editor without modification, but the following technical considerations and things to be aware of are documented below.
The iframe will have a different document and window than the admin page, which is now the parent window. Editor scripts are loaded in the admin page, so accessing the document or window to do something with the content will no longer work.
Most blocks written in React should continue to work properly, except if you rely on document or window. To fix, you need to create a ref to access the relative document (ownerDocument) or window (defaultView). Regardless of the iframe, it is good practice to do this and avoid the use of globals.
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>
);
}
If you attach event handlers, remember that the useEffect callback will not be called if the ref changes, so it is good practice to use the new useRefEffect API, which will call the given callback if the ref changes in addition to any dependencies passed.
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>
);
}
For the editor, scripts such as jQuery are loaded in the parent window (admin page), which is fine. When using these to interact with a block in the iframe, you should pass the element reference.
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>
);
}
Submit an issue or PR for the library to use ownerDocument and defaultView instead of the globals. Ideally, any library should allow initialization with an element in an iframe as the target. It’s never impossible. Feel free to contact us to mention the issue.
In the meantime, you can use the script that is loaded inside the iframe. We’ve loaded all front-end scripts in the iframe to fix these cases, but note that ideally you shouldn’t use scripts loaded in the iframe at all. You can use defaultView to access the script.
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;
// Use the script loaded in the iframe.
// Scripts are loaded asynchronously, so check if the script is loaded.
// After the dependencies have loaded, the block will re-render.
if ( ! defaultView.jQuery ) {
return;
}
defaultView.jQuery( element ).masonry( … );
return () => {
defaultView.jQuery( element ).masonry( 'destroy' );
}
} );
const blockProps = useBlockProps( { ref } );
return (
<div { ...blockProps }>
Hello world!
</div>
);
}