使用 Gutenberg InnerBlocks 创建出色的 WordPress 编辑体验并节省时间
已发表: 2023-02-12不要重复自己
对于开发人员来说,懒惰可能是一种美德。 一个制作精良、可重复使用的组件可以节省时间,并允许有效的重复使用和一致性。 这非常适合在 WordPress 中创建编辑体验,因为一致性为那些每天使用网站的人带来了熟悉和知识。
随着 Gutenberg 块编辑器的发布,WordPress 引起了很大的轰动,现在简称为“The Editor”。 对于内容编辑、起草和发布,它提供了极好的体验,远胜于旧的所见即所得。 作为一名开发人员,我想为我的客户提供最好的编辑体验,我想利用核心功能,我不想重新发明轮子。 幸运的是,WordPress 允许我们使用自定义块来完成所有这些事情。
输入 InnerBlocks
InnerBlocks 是 WordPress 编辑器中的一个很棒的功能。 开发人员可以使用段落、标题和按钮等核心块来为客户创建一致的体验。 客户无需重写文本部分和重新声明字段,而是获得他们熟悉的体验; 编辑和组合相同的块。 让我们看一下我们可能需要为客户端构建的块,看看 InnerBlocks 如何帮助我们实现它。
使用 InnerBlocks 构建块
让我们检查一下这个块并考虑如何构建它。 我们制作块时使用的一个常用工具是高级自定义字段 (ACF)。 ACF Pro 添加了自定义块支持,允许开发人员在 PHP 中为编辑器和前端编写块。 它还允许我们使用自定义字段——许多 WordPress 开发人员都熟悉的范例。 ACF 支持 InnerBlocks,这意味着我们可以创建自定义块,但甚至不需要为该标题、段落或按钮编写自定义代码。
借助 ACF Blocks,我们可以利用核心块为我们在该块中看到的所有文本提供出色的编辑体验。 另一个可能的选择是使用块模式,但作为出色的 WordPress 体验的开发人员和策展人,创建自定义块可以实现预期的目标。
区块注册码
下面是注册自定义 ACF 块的代码。 在此处阅读有关使用 acf_register_block_type 函数的更多信息。
<?php /** * Template for registering an ACF powered custom block. * * More info: https://www.advancedcustomfields.com/resources/blocks/ */ /** * Register the block. */ add_action( 'acf/init', function() { /** * ACF block registration options here: https://www.advancedcustomfields.com/resources/acf_register_block_type/ */ acf_register_block_type( array( 'name' => 'call-to-action-demo', // JS will register as: acf/{block-name}. 'title' => __( 'Call to Action ACF Demo', 'locale' ), 'description' => __( 'A custom ACF Call to Action block.', 'locale' ), 'render_template' => 'partials/blocks/call-to-action-demo.php', // Change to block template. 'category' => 'design', // Category in the block inserter. 'keywords' => array( 'action', 'buttons', 'cta' ), // Searchable keywords. 'supports' => array( 'align' => false, // Disable support for align. 'anchor' => true, // Enable support for anchor. 'jsx' => true, // Enable support for JSX. 'mode' => false, // Disable ACF block edit/preview mode switching as we are only using InnerBlocks for editable content. ), ) ); } );
让我们分解代码。 我们正在使用 acf_register_block_type 函数来注册我们的块,我们正在传递一个参数数组来定义我们的块并打开和关闭功能。
- 请注意,我们正在按照推荐的程序为我们的块添加一个唯一的名称,以及一个人类可读的标题和描述。 我们指向一个渲染模板,稍后我们将在其中为块设置我们的 PHP 模板。 我们还添加了一个类别,我们希望我们的块在块插入器中分组。
- 我们有可搜索的关键字,使块更容易在插入器中搜索。
- 我们添加了一个“supports”数组以启用设置 HTML 锚点等功能,并且我们关闭了对齐功能,因为该块将始终位于页面的中心。
- 支持数组是我们打开 InnerBlocks 魔法的地方! 将 'jsx' 设置为 true 告诉 ACF 我们将支持在我们的块中渲染 React jsx 模板。
让我们回顾一下我们块中需要的内容:
- 标题
- 段落文字
- 采取行动的按钮!
现在,通常我们会将字段分配给我们的块,如此处所述。 但在这种情况下,我们不需要——我们可以利用核心块来实现所有这些。 例如,如果块需要图像背景,则向块添加 ACF 图像字段可能是实现该目的的好方法。 不过现在,让我们继续我们的 InnerBlocks 示例并跳转到模板。
块模板
这是新块的模板。 确保将块注册指向块模板位置。 一种常见的做法是将它们放在主题的 partials 文件夹中。
<?php /** * ACF Call to Action example block template. * * More info: https://www.advancedcustomfields.com/resources/acf_register_block_type/ * * @param array $block The block settings and attributes. * @param string $content The block inner HTML (empty). * @param bool $is_preview True during AJAX preview. * @param (int|string) $post_id The post ID this block is saved to. * */ // Create id attribute allowing for custom "anchor" value. $block_ . $block['id']; if ( ! empty( $block['anchor'] ) ) { $block_id = $block['anchor']; } // Create class attribute allowing for custom "className" and "align" values. $class_name = 'acf-call-to-action-demo'; if ( ! empty( $block['className'] ) ) { $class_name .= ' ' . $block['className']; } if ( ! empty( $block['align'] ) ) { $class_name .= ' align' . $block['align']; } ?> <div class="<?php echo esc_html( $class_name ); ?>"> <div class="cta__inner"> <?php // Set up innerBlocks and provide a template. // Restrict InnerBlocks to allowed block list. $allowed_blocks = array( 'core/heading', 'core/paragraph', 'core/buttons' ); // Start InnerBlocks with a template. $template = array( array( 'core/heading', array( 'placeholder' => __( 'CTA Heading', 'locale' ), 'align' => 'center', 'level' => '2', ), ), array( 'core/paragraph', array( 'placeholder' => __( 'Add CTA text here', 'locale' ), 'align' => 'center', ), ), array( 'core/buttons', array( 'placeholder' => __( 'Add CTA buttons here', 'locale' ), 'align' => 'center', ), array( array( 'core/button', array( 'text' => __( 'Take action', 'locale' ), ), ), array( 'core/button', array( 'text' => __( 'Learn more', 'locale' ), ), ), ), ), ); // Echo out our JSX InnerBlocks compoennt for the editor. echo '<InnerBlocks allowedBlocks="' . esc_attr( wp_json_encode( $allowed_blocks ) ) . '" template="' . esc_attr( wp_json_encode( $template ) ) . '" templateLock="false" />'; ?> </div> </div>
让我们分解这段代码。
- 一开始,我们有一个 ACF 在其块指南中提供的通用块模板样板,例如对锚点和自定义类名的支持。
- InnerBlocks 组件可以接收属性。 我们在此模板中使用三个。
- 允许的块:我们添加一组允许的块来管理。 在我们的自定义块的 InnerBlocks 空间中,只有这些块可供选择。
- 模板:我们可以传递一个块数组,我们可以传递块的块属性,以便在它们首次加载到编辑器中时开始。
- 请注意,我们可以传递块属性来为我们的用户设置成功。 标题已经设置为 2 级,段落居中。
- 注意:核心块在 JavaScript 中始终被称为 {plugin}/blockname。 在我们的例子中,我们使用的是核心块,但如果您想使用自定义 ACF 块,您可以在块名称前写上“acf”。 请记住,在使用 InnerBlocks 时,我们会将此组件传递给使用 React 的 WordPress 编辑器。 这就是我们在这里用 JS 思考的原因。
- 另一个注意事项:请注意 core/buttons 块使用 InnerBlocks 本身! 我们实际上传递了一个包含两个核心/按钮块的数组!
- 模板锁定:模板锁定设置在 InnerBlocks 组件上。 如果我们将其设置为“全部”,则我们提供给 InnerBlocks 的模板中的任何块都不能移动或删除。 如果我们将其设置为“插入”,则只能移动其中的块,不能删除任何块,也不能添加新块。 如果我们将其设置为“false”,则 InnerBlocks 实例将被解锁,并且无论任何父模板锁如何,它都将被解锁(更多内容在后面)。 在 WordPress 块编辑器手册中阅读有关块模板的更多信息。 我们将更多地讨论块锁定。
我们有一个新街区! 毕竟,当然还有一些样式,示例块在编辑器中看起来像这样:
嵌套更深:使用嵌套的 InnerBlocks 进一步策划体验
我们有一个很棒的块,我们不需要设置所见即所得,甚至不需要处理模板中的自定义字段。 但是,如果我们想为客户进一步策划和微调这种体验怎么办? 假设我们收到一个请求,要求我们确保标题始终存在,因此始终有一个标题块,但后面的内容是灵活的,甚至可以包含一个列表或其他类型的块。 我们的目标是实现这种灵活性,同时保持一致性。
让我们在使用 InnerBlocks 时考虑一些规则:
- 单个块中可能只有一个 InnerBlocks 实例。
- InnerBlocks 中的多个块可以使用它们自己的 InnerBlocks 组件。
- InnerBlocks 可以通过 templateLock 属性锁定,但是嵌套块结构中更深层的 InnerBlocks 实例可以通过将 templateLock 设置为 false 再次解锁。
一种解决方案是创建一个新块作为 InnerBlocks 的包装器。 我们将该块包含在我们的父块模板中并锁定它,但通过将“templateLock”设置为 false 来显式解锁我们的包装块。 我们还可能希望通过为我们的块设置父块来确保这个特殊的包装块仅在我们选择的父块中可用。 我们可以允许该空间内的多个块类型来提供编辑器列表等,同时仍然只允许父级号召性用语块中的标题、按钮和我们的包装块。
单独的块锁定
WordPress 5.9 中的一项新功能是能够锁定模板中的各个块。 这是我们灵活但一致的问题的另一种可能解决方案。
这是我们的模板在锁定了一些单独的块后的样子:
<?php /** * ACF Call to Action example block template. * * More info: https://www.advancedcustomfields.com/resources/acf_register_block_type/ * * @param array $block The block settings and attributes. * @param string $content The block inner HTML (empty). * @param bool $is_preview True during AJAX preview. * @param (int|string) $post_id The post ID this block is saved to. * */ // Create id attribute allowing for custom "anchor" value. $block_ . $block['id']; if ( ! empty( $block['anchor'] ) ) { $block_id = $block['anchor']; } // Create class attribute allowing for custom "className" and "align" values. $class_name = 'acf-call-to-action-demo'; if ( ! empty( $block['className'] ) ) { $class_name .= ' ' . $block['className']; } if ( ! empty( $block['align'] ) ) { $class_name .= ' align' . $block['align']; } ?> <div class="<?php echo esc_html( $class_name ); ?>"> <div class="cta__inner"> <?php // Set up innerBlocks and provide a template. // Restrict InnerBlocks to allowed block list. $allowed_blocks = array( 'core/heading', 'core/paragraph', 'core/buttons' ); // Start InnerBlocks with a template. $template = array( array( 'core/heading', array( 'placeholder' => __( 'Heading', 'locale' ), 'align' => 'center', 'level' => '2', 'lock' => array( 'move' => true, // Block may nto be moved. 'remove' => true, // Block may not be removed. ), ), ), array( 'core/paragraph', array( 'placeholder' => __( 'Add CTA text here', 'locale' ), 'align' => 'center', ), ), array( 'core/buttons', array( 'placeholder' => __( 'Add CTA buttons here', 'locale' ), 'align' => 'center', 'lock' => array( 'move' => true, // Block may not be moved. 'remove' => true, // Block may not be removed. ), ), array( array( 'core/button', array( 'text' => __( 'Take action', 'locale' ), ), ), array( 'core/button', array( 'text' => __( 'Learn more', 'locale' ), ), ), ), ), ); // Echo out our JSX InnerBlocks compoennt for the editor. echo '<InnerBlocks allowedBlocks="' . esc_attr( wp_json_encode( $allowed_blocks ) ) . '" template="' . esc_attr( wp_json_encode( $template ) ) . '" />'; ?> </div> </div>
标题和按钮块不应再移动或删除。 一定要刷新编辑器,删除,然后再次添加块以获得模板更改。
奖励:本地制作相同的块
一旦构建过程到位,使用 React 制作原生 WordPress 块就出奇地容易,并且与在 php 中模板化块并没有太大区别。 设置块开发环境不在本文的讨论范围之内,但有许多资源可以帮助您入门,例如 WordPress Block Editor Handbook。 一旦您能够包含自定义块,您就可以使用 InnerBlocks 快速构建块。
这是我们在 React 中的号召性用语块 index.js 的示例。 您会看到我们上面讨论的与此处应用的 ACF 相同的策略。
import { __ } from '@wordpress/i18n'; import { registerBlockType } from '@wordpress/blocks'; import { InnerBlocks } from '@wordpress/block-editor'; import { useBlockProps } from '@wordpress/block-editor'; /** * Block Name. * Create an example Call to Action Block * Uses InnerBlocks for editable content within. */ export const blockName = 'call-to-action'; /** * Block Config. * Set basic params for controlling the editor. */ export const BLOCK_CONFIG = { // Set up the block template. CTA_TEMPLATE: [ [ 'core/heading', { placeholder: __('CTA Headline', 'locale'), align: 'center', level: 2, lock: { move: true, remove: true, }, }, ], [ 'core/paragraph', { placeholder: 'Optional CTA text', align: 'center', lock: { move: true, }, }, ], [ 'core/buttons', { lock: { move: true, remove: true, }, className: 'is-content-justification-center', align: 'center', // __experimentalLayout - https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/buttons/block.json layout: { type: 'flex', justifyContent: 'center', }, }, [ [ 'core/button', { text: 'Apply now', lock: { move: true, remove: true, }, }, ], ['core/button', { text: 'Learn more' }], ], ], ], // Set up the allowed blocks. ALLOWED_BLOCKS: ['core/paragraph', 'core/heading', 'core/buttons'], }; // Register the block via WP func. Change 'myplugin' to your plugin or theme. registerBlockType(`myplugin/${blockName}`, { title: __('Call to Action', 'locale'), // Change 'locale' to your locale for internationalization. description: __( 'Call to action block with headline and buttons', 'locale' ), keywords: [__('call'), __('action'), __('cta')], category: 'design', supports: { anchor: true, defaultStylePicker: false, html: false, align: false, }, attributes: { anchor: { type: 'string', default: '', }, }, transforms: {}, variations: [], edit: (props) => { const blockProps = useBlockProps({ className: `wp-block-myplugin-${blockName}`, }); return ( <div {...blockProps}> <div className="cta__inner"> <div className="cta__inner-blocks-wrapper"> <InnerBlocks template={BLOCK_CONFIG.CTA_TEMPLATE} allowedBlocks={BLOCK_CONFIG.ALLOWED_BLOCKS} renderAppender={false} /> </div> </div> </div> ); }, save: () => { return ( <div> <div className="cta__inner"> <InnerBlocks.Content /> </div> </div> ); }, });
去建造吧!
其他资源
- Bill Erickson 关于 InnerBlocks 的文章
- Bill Erickson 关于块编辑器的文章
- ACF 积木指南
- ACF 寄存器块类型文档
- WordPress 块编辑器手册
- WordPress 块编辑器手册:创建块教程