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 编辑器提供了几个重要优势:

iframe 化的文章编辑器将通过减少样式冲突和提高布局准确性,使区块和主题开发者的工作更加轻松。

文章编辑器何时以 iframe 形式工作?

虽然包括模板编辑器在内的大多数编辑器已采用 iframe 模式,但为了向后兼容,当前文章编辑器仅在满足以下条件时才会以 iframe 形式工作(由 useShouldIframe 钩子 决定):

简而言之,若您尚未在 iframe 编辑器中完成区块的全面测试,通过保持 apiVersion 为 2,可在多数情况下阻止文章编辑器以 iframe 形式运行。当确认您的区块能在 iframe 编辑器中正常工作后,即可迁移至 apiVersion 3。

文章编辑器何时会以 iframe 方式工作?

在 WordPress 7.0 中,无论已注册区块的 apiVersion 如何,文章编辑器都计划始终以 iframe 方式工作

在此之前,为了鼓励开发者在 iframe 编辑器中测试,WordPress 6.9 引入了以下开发者警告和模式变更:

如何在 iframe 文章编辑器中测试你的区块

所有核心区块已使用 apiVersion 3,因此只需将你的 apiVersion 更改为 3,即可让你的区块在 iframe 文章编辑器中工作。

但请确保不存在其他注册版本为 2 或更低的第三方区块。如果注册了版本 2 或更低的区块,文章编辑器可能无法作为 iframe 编辑器正常工作。

iframe 编辑器的技术考量

大多数区块无需修改即可在 iframe 编辑器中运行,但以下技术注意事项和需知事项已记录如下。

文档与窗口

iframe 将拥有与当前作为父窗口的管理页面不同的 documentwindow。编辑器脚本加载于管理页面中,因此通过访问 documentwindow 来操作内容将不再有效。

大多数使用 React 编写的区块应能继续正常工作,除非您依赖 documentwindow。修复方法是创建引用以访问相关文档 (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,建议其使用 ownerDocumentdefaultView 替代全局对象。理想情况下,任何库都应允许以 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>
    );
}