ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Gutenberg 块开发实战指南:从零构建 WordPress 块类型的完整教程

Gutenberg 块开发实战指南:从零构建 WordPress 块类型的完整教程 Gutenberg 块开发实战指南从零构建 WordPress 块类型的完整教程【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg本篇指南以 GutenbergWordPress 块编辑器官方块开发教程为主线带你从最简单的块类型示例出发逐步掌握动态块渲染、嵌套块InnerBlocks、样式加载以及扩展 Query Loop 块等进阶能力。读完本文你将能够独立搭建块开发环境、编写可发布的 WordPress 块插件并理解registerBlockType与register_block_type前后端注册机制、render_callback服务端渲染链路以及block.json元数据驱动的资源加载方式。教程概览从最小示例开始增量构建块类型docs/how-to-guides/block-tutorial/README.md是 Gutenberg 块开发教程的入口。它开宗明义地指出本教程的目的是逐步讲解创建一个新块类型的基础知识——从最简单的示例开始每一节都在前文基础上增量叠加常见的块功能例如属性attributes、动态渲染、嵌套块、样式与查询扩展等。跟随本教程时你可以下载与之配套的示例 WordPress 插件即block-development-examples仓库打包的 zip 文件内含本教程全部示例在自己的站点上逐一实验。教程鼓励的做法是每完成一步就用自己的想法修改示例、观察其对块行为产生的影响以此加深理解。教程中的代码示例以两种格式呈现可通过代码块上方的页签切换JSX使用 JSX 语法的 JavaScript 代码需要构建步骤build step将代码编译为浏览器可执行的格式Plain无需构建的经典 JavaScript 写法。需要强调的是创建块或扩展编辑器并不强制要求使用 JSX经典 JavaScript 完全可行。不过一旦熟悉 JSX 与构建步骤多数开发者会发现 JSX 更易读、易写因此社区绝大多数示例都采用 JSX 语法。关于构建步骤的完整说明可参阅 Working with JavaScript for the Block Editor。构建步骤与 wp-scripts 概览现代块开发几乎都会用到构建工具链。ESNext 与 JSX 语法无法被浏览器直接执行必须经过转换、打包、优化产出生产环境可用的资源。官方推荐的方案是wordpress/scriptswp-scripts包——它预配置了 webpack 与 Babel你基本无需手写构建配置生产模式npm run build压缩输出、减小文件体积、提升浏览器加载性能适合部署上线开发模式npm start跳过压缩以方便调试、生成 source map 辅助错误追踪并监听源文件变化自动增量重建实现实时预览。若确有特殊需求wp-scripts也允许你通过自定义webpack.config.js覆盖默认构建行为。此外构建过程天然支持 JavaScript 模块化可将代码分散到多个文件构建后合并为精简的 bundle。客户端注册registerBlockType客户端注册块的入口是wordpress/blocks包的registerBlockType方法。该方法的重载签名定义在 packages/blocks/src/api/registration.ts支持两种调用形式传入块名称字符串 客户端设置对象直接传入包含block.json元数据的对象配合构建流程可import metadata from ./block.json。registerBlockType( blockNameOrMetadata, settings )返回注册成功的块类型BlockType失败时返回undefined。其中最重要的两个设置属性是edit编辑器内渲染块的 React 组件save返回保存到数据库的静态 HTML 标记的函数。前置准备搭建环境与脚手架块插件动手之前需要准备代码编辑器、Node.js 开发工具链以及本地 WordPress 环境。官方推荐的本地环境方案是wp-env详见 Quick Start Guide 与 Tutorial: Build your first block。创建新块的最快方式是使用wordpress/create-block脚手架npx wordpress/create-blocklatest copyright-date-block --variantdynamic cd copyright-date-block该命令会在插件目录下生成copyright-date-block文件夹包含块插件的全部初始文件并以copyright-date-block作为块的 slug在 WordPress 内唯一标识该块。--variantdynamic表示脚手架产出动态渲染块不会生成save.js。之后在插件目录内运行npm run start启动开发模式/src目录下每个文件的改动都会被实时构建。脚手架的产物中block.json是块的中枢配置文件集中声明块的名称、标题、图标、分类、属性attributes、支持的 UI 面板supports、脚本与样式资源等。例如{ $schema: https://schemas.wp.org/trunk/block.json, apiVersion: 3, name: create-block/copyright-date-block, title: Copyright Date Block, category: widgets, description: Display your sites copyright date., attributes: { showStartingYear: { type: boolean }, startingYear: { type: string } }, supports: { color: { background: false, text: true }, html: false, typography: { fontSize: true } }, textdomain: copyright-date-block, editorScript: file:./index.js, render: file:./render.php }块注册既发生在服务端PHP也发生在客户端JS。最佳实践是双侧注册服务端注册才能让动态渲染、Block Supports、Block Hooks、样式变体以及theme.json样式等功能正常工作。服务端注册在init钩子上完成相关 API 与示例可参阅 Registration of a block。深入一动态块Dynamic Blocks动态块是指在前端渲染时即时构建结构与内容的块其完整实现见 creating-dynamic-blocks.md。它有两类典型用途内容需要随站点状态自动更新例如最新文章块每当有新文章发布所有使用该块的位置都会自动更新无需编辑旧文章代码更新应立刻反映到前端例如修改了块的 HTML 结构新增 class、元素或改变布局动态块会让全站所有实例立即生效而静态块则会触发 Gutenberg 的校验流程让用户看到This block appears to have been modified externally提示。save 返回 null 与属性存储对多数动态块而言客户端的save回调应返回null这告诉编辑器只把块属性attributes保存到数据库不保存 HTML 输出。这些属性随后会被传入服务端渲染回调由服务端决定如何展示。返回null还意味着编辑器跳过块标记校验避免因标记频繁变化而报错。若动态块中使用了InnerBlocks则必须在save回调里通过InnerBlocks.Content /保存子块内容。你也可以选择保存一份 HTML 表示——它平时会被服务端渲染回调的输出替换但在块停用或渲染回调被移除时作为兜底渲染。属性的作用域很广最新文章块可以把显示篇数存为属性内容展示型块则可以把标题文本、段落文本、图片、URL 等逐项存为属性。完整示例动态渲染最新文章先看客户端JSX部分——它通过wordpress/data的useSelect从corestore 拉取文章数据在编辑器中实时预览import { registerBlockType } from wordpress/blocks; import { useSelect } from wordpress/data; import { useBlockProps } from wordpress/block-editor; registerBlockType( gutenberg-examples/example-dynamic, { apiVersion: 3, title: Example: last post, icon: megaphone, category: widgets, edit: () { const blockProps useBlockProps(); const posts useSelect( ( select ) { return select( core ).getEntityRecords( postType, post ); }, [] ); return ( div { ...blockProps } { ! posts Loading } { posts posts.length 0 No Posts } { posts posts.length 0 ( a href{ posts[ 0 ].link } { posts[ 0 ].title.rendered } /a ) } /div ); }, } );由于是动态块客户端无需覆盖默认save实现真正决定前端输出的是服务端register_block_type的render_callback?php /** * Plugin Name: Gutenberg examples dynamic */ function gutenberg_examples_dynamic_render_callback( $block_attributes, $content ) { $recent_posts wp_get_recent_posts( array( numberposts 1, post_status publish, ) ); if ( count( $recent_posts ) 0 ) { return No posts; } $post $recent_posts[ 0 ]; $post_id $post[ID]; return sprintf( a classwp-block-my-plugin-latest-post href%1$s%2$s/a, esc_url( get_permalink( $post_id ) ), esc_html( get_the_title( $post_id ) ) ); } function gutenberg_examples_dynamic() { $asset_file include( plugin_dir_path( __FILE__ ) . build/index.asset.php); wp_register_script( gutenberg-examples-dynamic, plugins_url( build/block.js, __FILE__ ), $asset_file[dependencies], $asset_file[version] ); register_block_type( gutenberg-examples/example-dynamic, array( api_version 3, editor_script gutenberg-examples-dynamic, render_callback gutenberg_examples_dynamic_render_callback ) ); } add_action( init, gutenberg_examples_dynamic );这段代码有几点值得注意edit函数仍然展示编辑器内的块表示——它可以与最终渲染结果完全不同完全由块作者决定内置save函数返回null因为渲染在服务端完成服务端渲染回调接收块与块内内容两个参数返回标记字符串机制与短代码shortcode非常相似常用的颜色、边框、间距等自定义项应优先借助 block supports 实现而不是手写。编辑器内实时渲染ServerSideRender从 Gutenberg 2.8 起ServerSideRender块即wordpress/server-side-render包允许编辑器内直接调用 PHP 服务端渲染所见即所得import { registerBlockType } from wordpress/blocks; import ServerSideRender from wordpress/server-side-render; import { useBlockProps } from wordpress/block-editor; registerBlockType( gutenberg-examples/example-dynamic, { apiVersion: 3, title: Example: last post, icon: megaphone, category: widgets, edit: function ( props ) { const blockProps useBlockProps(); return ( div { ...blockProps } ServerSideRender blockgutenberg-examples/example-dynamic attributes{ props.attributes } / /div ); }, } );注意这段代码改用wp-server-side-render依赖而不再需要wp-data因此 PHP 中的脚本依赖数组也要相应更新使用wp-scripts构建时依赖可自动生成。该组件的完整文档见 packages/server-side-render/README.md。需要明确的是服务端渲染是回退方案客户端JS渲染始终是首选——客户端渲染更快也更利于编辑器内操作。深入二嵌套块与 InnerBlocks通过InnerBlocks组件你可以创建一个容纳其他块的单一块Columns 块、Social Links 块等都是典型例子。其完整指南见 nested-blocks-inner-blocks.md。注意一个块只能包含一个InnerBlocks组件。基础用法如下import { registerBlockType } from wordpress/blocks; import { InnerBlocks, useBlockProps } from wordpress/block-editor; registerBlockType( gutenberg-examples/example-06, { // ... edit: () { const blockProps useBlockProps(); return ( div { ...blockProps } InnerBlocks / /div ); }, save: () { const blockProps useBlockProps.save(); return ( div { ...blockProps } InnerBlocks.Content / /div ); }, } );常用属性allowedBlocks、orientation、defaultBlock、templateallowedBlocks在block.json的allowedBlocks字段之外进一步限制可直接插入的子块且支持按每个块的实例动态计算例如由块属性决定const { allowedBlocks } attributes; //... InnerBlocks allowedBlocks{ allowedBlocks } /;如果允许列表恒定不变应优先使用block.json中的allowedBlocks设置。orientation默认InnerBlocks按垂直列表展示子块若通过 CSS flex/grid 将子块排布为横向可设置orientationhorizontal。该属性不影响布局本身但会让子块的移动按钮呈横向显示并确保拖放行为正确InnerBlocks orientationhorizontal /defaultBlock 与 directInsert默认点击追加器appender会弹出允许块的选择列表可以通过defaultBlock指定点击追加器时默认插入的块及其属性并用directInsert强制跳过插入器下拉包括已注册的插入器变体直接插入InnerBlocks defaultBlock{ { name: core/paragraph, attributes: { content: Lorem ipsum... } } } directInsert /当allowedBlocks解析为单一且无变体的块类型时追加器本就会直接插入directInsert冗余。template 与 templateLock用template属性为 InnerBlocks 预填一组块可在其中设置占位属性用templateLock锁定模板。以下示例是书评模板const MY_TEMPLATE [ [ core/image, {} ], [ core/heading, { placeholder: Book Title } ], [ core/paragraph, { placeholder: Summary } ], ]; //... edit: () { return ( InnerBlocks template{ MY_TEMPLATE } templateLockall / ); },templateLockall完全锁定模板任何改动都不允许insert禁止再插入新块但允许重排现有块。InnerBlocks组件的更多细节可查看 packages/block-editor/src/components/inner-blocks。与之互补的是文章模板post template它按文章类型预载整个编辑器的一组块可把整篇文章锁定为指定模板而InnerBlocks模板只作用于你创建的那个块内部add_action( init, function() { $post_type_object get_post_type_object( post ); $post_type_object-template array( array( core/image ), array( core/heading ) ); } );块的三种嵌套关系parent、ancestor 与 allowedBlocks用 InnerBlocks 构建嵌套块的常见模式是让某个块只有在其父块被插入时才可用。Gutenberg 提供三种关系全部在block.json中声明parent嵌套块只能作为父块的直接子块插入。例如 Column 块声明parent: [ core/columns ]因此它只在 Columns 块内部可用不会出现在块插入器中ancestor嵌套块可以出现在祖先块的层级树任意位置不必是直接子块但同样受限于插入器。例如 Comment Author Name 块声明ancestor: [ core/comment-template ]allowedBlocks方向相反——声明哪些块可作为本块的直接子块。例如 Navigation 块限定[ core/navigation-link, core/search, core/social-links, core/page-list, core/spacer ]。自定义块还可以通过blocks.registerBlockType过滤器把自己加入 Navigation 的允许子块列表。parent与ancestor的关键区别在于parent限定更严格ancestor在嵌套层级上更灵活。用 useInnerBlocksProps 钩子替代组件wordpress/block-editor还导出了useInnerBlocksProps钩子支持InnerBlocks组件的全部能力并像useBlockProps一样让你完全掌控内块区域的标记。重要约束useBlockProps必须在useInnerBlocksProps之前调用否则前者会返回空对象。基础用法import { registerBlockType } from wordpress/blocks; import { useBlockProps, useInnerBlocksProps } from wordpress/block-editor; registerBlockType( gutenberg-examples/example-06, { // ... edit: () { const blockProps useBlockProps(); const innerBlocksProps useInnerBlocksProps(); return ( div { ...blockProps } div {...innerBlocksProps} / /div ); }, save: () { const blockProps useBlockProps.save(); const innerBlocksProps useInnerBlocksProps.save(); return ( div { ...blockProps } div {...innerBlocksProps} / /div ); }, } );钩子还支持直接接收useBlockProps的返回值从而减少 DOM 层级edit: () { const blockProps useBlockProps(); const innerBlocksProps useInnerBlocksProps( blockProps ); return ( div {...innerBlocksProps} / ); },更进一步你可以解构出children属性真实的内子块把自定义元素与子块放在同一层级渲染edit: () { const blockProps useBlockProps(); const { children, ...innerBlocksProps } useInnerBlocksProps( blockProps ); return ( div {...innerBlocksProps} { children } {/* Insert any arbitrary html here at the same level as the children */} /div ); },渲染结果大致如下div !-- Inner Blocks get inserted here -- !-- The custom html gets rendered on the same level -- /div深入三为块添加样式与样式表块通常会向文章内容插入需要定制的 HTML 标记。官方指南 applying-styles-with-stylesheets.md 介绍了两种主要方式均基于useBlockProps钩子其细节可参考 block wrapper 文档。方法一内联样式useBlockProps接收一个样式对象将其转为块包装元素上的属性import { registerBlockType } from wordpress/blocks; import { useBlockProps } from wordpress/block-editor; registerBlockType( gutenberg-examples/example-02-stylesheets, { edit() { const greenBackground { backgroundColor: #090, color: #fff, padding: 20px, }; const blockProps useBlockProps( { style: greenBackground } ); return ( p { ...blockProps }Hello World (from the editor, in green)./p ); }, save() { const redBackground { backgroundColor: #900, color: #fff, padding: 20px, }; const blockProps useBlockProps.save( { style: redBackground } ); return ( p { ...blockProps }Hello World (from the frontend, in red)./p ); }, } );内联样式适合少量 CSS样式较多时建议放到独立样式表文件。方法二块类名classnameuseBlockProps会自动为块生成类名以块名加上wp-block-前缀并将命名空间分隔符/替换为-。例如块名gutenberg-examples/example-02-stylesheets会得到类名wp-block-gutenberg-examples-example-02-stylesheets——虽然较长但能有效避免与其他块冲突。import { registerBlockType } from wordpress/blocks; import { useBlockProps } from wordpress/block-editor; registerBlockType( gutenberg-examples/example-02-stylesheets, { edit() { const blockProps useBlockProps(); return ( p { ...blockProps }Hello World (from the editor, in green)./p ); }, save() { const blockProps useBlockProps.save(); return ( p { ...blockProps }Hello World (from the frontend, in red)./p ); }, } );通过 block.json 加载样式表与脚本一样样式也可以在block.json中声明构建后会自动入队editorStyle仅在编辑器视图加载style编辑器与前端都加载当块被使用时viewStyle仅当前端使用该块时加载。若编辑器内容处于 iframe 中style与editorStyle都会加载进 iframe而editorStyle还会在 iframe 外加载因此也可用于编辑器 UI。{ apiVersion: 3, name: gutenberg-examples/example-02-stylesheets, title: Example: Stylesheets, icon: universal-access-alt, category: layout, editorScript: file:./block.js, editorStyle: file:./editor.css, style: file:./style.css }配套的样式文件示例/* editor.css - green background */ .wp-block-gutenberg-examples-example-02-stylesheets { background: #090; color: white; padding: 20px; }/* style.css - red background */ .wp-block-gutenberg-examples-example-02-stylesheets { background: #900; color: white; padding: 20px; }使用wordpress/scripts时需要在对应的 JavaScript 文件里import样式构建工具才会处理edit.js中import ./editor.scss;index.js中import ./style.scss;view.js中import ./view.scss;交互式块模板。若文件较多也可以退回标准wp_enqueue_style配合enqueue_block_editor_assets仅编辑器或enqueue_block_assets前端与编辑器钩子加载。深入四扩展 Query Loop 块Query Loop 块是功能强大的查询工具可遍历指定文章列表并为每篇文章渲染一组继承其上下文的块例如按分类遍历文章并显示特色图片。但正因其强大普通用户面对查询等术语会望而生畏。默认提供的Post List 变体就是很好的示范——用户无需接触技术细节即可使用。为此扩展者往往需要为 Query Loop 提供定制变体自带预设、附加设置并隐藏与其用例无关的选项。完整教程见 extending-the-query-loop-block.md。第一步注册带默认值的块变体以注册了book自定义文章类型的插件为例创建一个默认查询图书列表的变体。三步走为core/query注册带默认值的变体、定义变体布局、用namespace属性配合isActive标记变体。const MY_VARIATION_NAME my-plugin/books-list; registerBlockVariation( core/query, { name: MY_VARIATION_NAME, title: Books List, description: Displays a list of books, isActive: ( { namespace, query } ) { return ( namespace MY_VARIATION_NAME query.postType book ); }, icon: /** An SVG icon can go here*/, attributes: { namespace: MY_VARIATION_NAME, query: { perPage: 6, pages: 0, offset: 0, postType: book, order: desc, orderBy: date, author: , search: , exclude: [], sticky: , inherit: false, }, }, scope: [ inserter ], } );要点解读变体的query是整体替换块的默认query对象而非合并因此必须包含变体依赖的全部属性而不只是改动项务必设置postType缺失时块在编辑器和前端都会查询普通文章scope: [ inserter ]让变体像普通块一样出现在编辑器插入器中配合自定义title、description、SVGicon变体看起来就是一个完全品牌化的独立块namespace是 Query Loop 暴露的专用属性在块实现内部不做任何事专门用于扩展者识别和限定自己的变体。第二步定义变体布局Query Loop 的scope理论支持block字符串在插入块后由变体选择器拾取但当前不推荐使用与模式配合时所选模式的全部属性会被采用唯独postType和inherit除外容易引发冲突、产生无效变体。替代方案有两个方案 A提供默认innerBlocks——变体插入时直接以这些内块为起点跳过 Query Loop 的模式设置阶段innerBlocks: [ [ core/post-template, {}, [ [ core/post-title ], [ core/post-excerpt ] ], ], [ core/query-pagination ], [ core/query-no-results ], ],方案 B注册与变体连接的模式——将变体名称以core/query/$variation_name形式Query Loop 名称 变体名写入模式的blockTypes属性。Query Loop 会检测自身是否有激活变体及其专属模式有则只推荐这些模式否则退回默认模式。若未提供innerBlocks用户选择Start blank时还可通过连接变体建议将目标变体的scope设为[block]namespace设为包含主变体name的数组。第三步用 isActive 让 Gutenberg 识别变体插入对用户透明但 Gutenberg 内核仍将其识别为 Query Loop 块例如在树状视图中显示为 Query Loop。isActive用于依据块属性判断某个变体是否激活{ /** ...variation properties */ isActive: ( { namespace, query } ) { return ( namespace MY_VARIATION_NAME query.postType book ); }, }不要只比较postType——那会撒网过宽其他插件可能也基于book类型发布变体或用户手动在编辑器里把类型改为book时也不应触发识别。更简洁的做法是只比对namespaceisActive也接受属性名数组{ /** ...variation properties */ attributes: { /** ...variation attributes */ namespace: my-plugin/books-list, }, isActive: [ namespace ], }禁用无关查询控件与 taxQuery 结构你的自定义文章类型可能有独特要求某些标准控件无关甚至不受支持。变体支持allowedControls属性传入要展示的控件键数组不传则默认全部显示。以 Gutenberg 14.2 起可用的控件为例inherit是否从模板继承查询的开关postType可选文章类型下拉框order查询排序下拉框sticky置顶文章处理下拉框taxQuery当前文章类型的分类筛选含每个分类的包含/排除控件author按作者筛选的输入框search按关键词筛选的输入框format按文章格式数组筛选的输入框parents按父级实体筛选的输入框。示例——只保留相关控件并禁用postType避免用户误改破坏查询{ /** ...variation properties */ allowedControls: [ inherit, order, taxQuery, search ], }设为空数组[]可隐藏全部上述控件。taxQuery属性同时支持分类术语的包含与排除结构如下{ query: { taxQuery: { include: { category: [1, 2, 3], // Include posts with these category IDs. post_tag: [10, 20] // Include posts with these tag IDs. }, exclude: { category: [5, 6], // Exclude posts with these category IDs. post_tag: [15] // Exclude posts with these tag IDs. } } } }UI 上用户会看到[Taxonomy]包含与Exclude: [Taxonomy]排除两套控件两者互斥——一个分类里选中的术语不会出现在另一侧的候选中。添加自定义控件并打通前后端查询禁用核心控件后可以通过 React HOC 块过滤器editor.BlockEdit挂上自己的面板与控件例如为图书插件添加BookAuthorSelectorimport { InspectorControls } from wordpress/block-editor; export const withBookQueryControls ( BlockEdit ) ( props ) { // We only want to add these controls if it is our variation, // so here we can implement a custom logic to check for that, similar // to the isActive function described above. return isMyBooksVariation( props ) ? ( BlockEdit keyedit { ...props } / InspectorControls BookAuthorSelector / { /** Our custom component */ } /InspectorControls / ) : ( BlockEdit keyedit { ...props } / ); }; addFilter( editor.BlockEdit, core/query, withBookQueryControls );query对象内的任何额外参数如bookAuthor: J. R. R. Tolkien都可作为自定义查询参数。要让自定义查询同时作用于前后端需要两条路径前端Query Loop 主要通过 Post Template 块接收属性构建查询可挂钩query_loop_block_query_vars过滤器按你的参数修改查询务必仅在你的变体上生效避免影响其他 Query Loop 块编辑器端Query Loop 通过 WordPress REST API 获取预览文章额外参数会作为查询参数传给 API。因此要么该参数受 REST API 支持要么挂rest_{$post_type}_query过滤器例如rest_book_query处理。至此一个完全定制、功能完整的 Query Loop 变体就完成了。结语通往自定义块的完整路径从 block-tutorial/README.md 出发本文沿着官方教程的脉络覆盖了块开发的完整路径理解 JSX/Plain 两种写法与构建流程、用create-block脚手架项目、掌握动态渲染save返回nullrender_callback与ServerSideRender实时预览、用InnerBlocks与useInnerBlocksProps构建嵌套块与三种父子关系、通过useBlockProps与block.json灵活加载样式最后以 Query Loop 变体为例展示了块变体 API 的深度定制能力。在此基础上你可以继续深入 Tutorial: Build your first block一个完整的 Copyright Date Block 实战属性、InspectorControls 面板、静态动态双渲染与跨年份内容兜底策略、Working with JavaScript for the Block Editor无构建流程的经典 JS 接入方式与依赖管理以及 Registration of a blockwp_register_block_types_from_metadata_collection等新版服务端注册 API逐步把教程中的示例打磨成自己的生产级块插件。【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表