使用 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 塊編輯器手冊:創建塊教程