在WordPress块编辑器中添加自定义欢迎指南

莱昂纳多·洛索维兹的阿凡达
莱昂纳多·洛索维兹(更新日期:)

我正在创建一个WordPress插件,在使用它时会有一个小小的学习曲线。我想给用户一个如何使用插件的入门,但我想避免让用户转向插件网站上的文档,因为这会让他们失去体验。

最好的是用户在安装插件后立即开始使用它,但在积极使用时可以获得有用的提示。WordPress中没有类似的本地功能,但我们可以做一些东西,因为WordPres具有这样的超级灵活性。

这就是我们的想法。我们将把文档直接烘焙到插件中,并使其在块编辑器中易于访问。这样,用户就可以立即使用插件,同时可以直接在他们工作的地方回答常见问题。 

我的插件通过几个自定义帖子类型(CPT)进行操作。我们要构建的基本上是用户在使用这些CPT时得到的弹出模式。 

WordPress块编辑器是在React中构建的,它使用的组件可以根据不同的情况进行定制和重用。这就是我们正在做的事情-让我们称之为<指南>组件-它的行为类似于模态,但由几个页面组成,用户可以在其中进行分页。

WordPress本身有一个<指南>第一次打开块编辑器时显示欢迎指南的组件:

屏幕截图显示WordPress块编辑器顶部的一个模式,欢迎用户首次使用该编辑器。
当用户首次加载编辑器时,WordPress显示一个带有块编辑器使用说明的模式。

指南是一个装满内容的容器,内容被分解成单独的页面。换句话说,这正是我们想要的。这意味着我们不必重新启动这个项目;我们可以在自己的插件中重用这个相同的概念。

让我们就这样做吧。 

我们想要实现的目标

在我们找到解决方案之前,让我们谈谈最终目标。

该设计满足插件的要求,该插件是WordPress的GraphQL服务器。该插件提供了各种CPT,这些CPT通过自定义块进行编辑,而自定义块又是通过模板定义。总共有两个块:一个称为“GraphiQL客户端”,用于输入GraphQL查询,另一个名为“持久化查询选项”,用于自定义执行行为。

由于为GraphQL创建查询不是一项简单的任务,我决定将guide组件添加到该CPT的编辑器屏幕中。它在文档设置中作为一个名为“欢迎指南”的面板提供

屏幕截图显示WordPress编辑器,文档设置面板在右栏中打开。设置中突出显示了欢迎指南选项卡。

打开面板,用户就会得到一个链接。该链接将触发模态。

打开了欢迎指南选项卡的特写截图,显示了一个链接,上面写着“Open guide:Creating Persisted Queries”

对于模式本身,我决定在第一页上显示一个关于使用CPT的教程视频,然后在随后的页面上详细描述CPT中可用的所有选项。

屏幕截图显示了块编辑器中打开的自定义模式,并包含如何使用插件的嵌入式视频。

我相信这种布局是向用户显示文档的有效方式。这是一个偏僻的地方,但仍然方便地接近行动。当然,我们可以使用不同的设计,甚至可以将模式触发器放置在其他地方,使用不同的组件,而不是重新调整用途<指南>,但这很好。

规划实施

实施包括以下步骤:

  1. 构建新脚本以注册自定义侧栏面板
  2. 在编辑器上仅显示自定义帖子类型的自定义侧栏面板
  3. 创建指南
  4. 向指南添加内容

我们开始吧!

步骤1:搭建脚本

从WordPress 5.4开始,我们可以使用一个名为<插件文档设置面板>添加面板在编辑器的“文档”设置上,如下所示:

const{registerPlugin}=wp.plugins;const{PluginDocumentSettingsPanel}=wp.editPost; const插件文档设置面板演示=()=>(<插件文档设置面板name=“custom-panel”title=“自定义面板”className=“custom-panel”  >自定义面板内容</PluginDocumentSettingsPanel>);registerPlugin('plugin-document-setting-panel-demo'{渲染:PluginDocumentSettingPanelDemo,icon:'棕榈树',} );

如果您对块编辑器有经验并且已经知道如何执行此代码,那么可以跳过。我用块编辑器编写代码还不到三个月,使用React/npm/webpack对我来说是一个全新的世界——这个插件是我第一个使用它们的项目!我发现古腾堡回购对于像我这样的初学者来说并不总是足够的,有时文档会完全丢失,所以我不得不深入研究源代码以找到答案。

当组件的文档指示使用上述代码时,我不知道下一步该怎么做,因为<插件文档设置面板>不是块,我无法构建新块或在其中添加代码。此外,我们正在与JSX合作,这意味着我们需要JavaScript构建步骤编译代码。

然而,我确实找到了等效ES5代码以下为:

var el=wp.element.createElement;var __=wp.i18n.__;var寄存器插件=wp.plugins.registerPlugin;var PluginDocumentSettingsPanel=wp.editPost。插件文档设置面板;函数MyDocumentSettingsPlugin(){返回el(插件文档设置面板,    {className:'我的文档设置插件',title:'我的面板',    },__(“我的文档设置面板”)  );}registerPlugin('my-document-setting-plugin'{渲染:MyDocumentSettingsPlugin} );

ES5代码不需要编译,所以我们可以像加载WordPress中的任何其他脚本一样加载它。但我不想用它。我想要ESNext和JSX的全面、现代的体验。

所以我的想法是这样的:我不能使用砌块脚手架工具因为它不是一个块,而且我不知道如何编译脚本(我当然不会自己设置webpack)。这意味着我被卡住了。

但是等等!块和正则脚本的唯一区别就是它们在WordPress中的注册方式。块的注册方式如下:

wp_register_script($blockScriptName,$blockScriptURL,$dependencies,$version);register_block_type('my-namespace/my-block'[“editor_script”=>$blockScriptName,]);

正则脚本注册如下:

wp_register_script($scriptName,$scriptURL,$dependencies,$version);wp_enqueue_script($scriptName);

我们可以使用任何块脚手架工具来修改内容,然后注册一个常规脚本而不是块,这样我们就可以访问webpack配置来编译ESNext代码。这些可用工具包括:

我选择使用@wordpress/创建块包,因为它是由开发古腾堡的团队维护的。

为了搭脚手架,我们执行这个在命令行中:

npm初始化@wordpress/block

完成所有信息提示(包括块的名称、标题和描述)后,该工具将生成一个单块插件,带有一个包含类似代码的入口PHP文件:

/***注册所有块资产,以便它们可以通过块编辑器排队*在相应的上下文中。 **@参见https://developer.wordpress.org/block-editor/tutorials/block-tutorial/applying-styles-with-stylesheets(带样式表)/ */函数my_namespace_my_block_block_ init(){$dir=目录名(__FILE__);$script_asset_path=“$dir/build/index.asset.php”;如果(!file_exists($script_assetpath)){抛出新错误('您需要首先为“my-namespace/my-block”块运行`npm-start`或`npm-run-build`。'    );  }$index_js='build/index.js';$script_asset=要求($script_aasset_path);wp_注册_脚本('我的命名空间我的块块编辑器',插件url($index_js,__FILE_),$script_asset[“依赖关系”],$script_asset[“版本”]  );$editor_css=“editor.css”;wp注册样式(“my-namespace-my-block-block-editor”,插件url($editor_css,__FILE_),数组(),文件时间(“$dir/$editor_css”)  );$style_css='style.css';wp注册样式(“my-namespace-my-block-block”,插件url($style_css,__FILE_),数组(),文件时间(“$dir/$style_css”)  );register_block_type('my-namespace/my-block',数组(“editor_script”=>“my-namespace-my-block-block-editor”,“editor_style”=>“my-namespace-my-block-block-editor”,“style”=>“my-namespace-my-block-block”,));}add_action('init','my_namespace_my_block_block.init');

我们可以将此代码复制到插件中,并对其进行适当修改,将块转换为常规脚本。(请注意,我还在删除CSS文件,但如果需要,可以保留它们。)

函数my_script_init(){$dir=目录名(__FILE__);$script_asset_path=“$dir/build/index.asset.php”;如果(!file_exists($script_assetpath)){引发新错误('您需要先为“我的脚本”脚本运行'npm-start'或'npm-run-build'。'    );  }$index_js='build/index.js';$script_asset=要求($script_aasset_path);wp_注册_脚本(“我的脚本”,plugins_url($index_js,__FILE__),$script_asset[“依赖关系”],$script_asset[“版本”]  );wp_排队_脚本('我的脚本'  );}add_action('init','my_script_init');

让我们复制package.json包文件覆盖:

{“name”:“my-block”,“版本”:“0.1.0”,“description”:“这是我的街区”,“作者”:“WordPress贡献者”,“许可证”:“GPL-2.0-or-later”,“main”:“build/index.js”,“脚本”:{“构建”:“wp-scripts构建”,“format:js”:“wp-scripts format-js”,“lint:css”:“wp-scripts lint-style”,“lint:js”:“wp-scripts lint-js”,“start”:“wp-scripts start”,“包更新”:“wp-scripts包更新”  },“devDependencies”(开发依赖项):{“@wordpress/scripts”:“^9.1.0”  }}

现在,我们可以替换文件的内容源代码/索引.js使用上面的ESNext代码注册<插件文档设置面板>组件。运行时npm启动(或npm运行构建用于生产)代码将编译为构建/index.js

还有最后一个问题需要解决:<插件文档设置面板>组件不是静态导入的,而是从wp.editPost(wp.edit发布),自水处理是WordPress在运行时加载的全局变量,此依赖项不存在于索引资产.php(在构建期间自动生成)。我们必须手动将依赖项添加到wp-edit-post文件注册脚本时使用脚本,以确保它在我们之前加载:

$dependencies=数组_合并($script_asset[“依赖关系”],  [“wp-edit-post”,  ]);wp_注册_脚本(“我的脚本”,插件url($index_js,__FILE_),$依赖项,$script_asset[“版本”]);

现在脚本设置就绪!

该插件可以随着古腾堡无情的开发周期进行更新。运行npm运行包更新更新npm依赖项(因此webpack配置,它在包上定义“@wordpress/scripts”)到最新支持的版本。

此时,您可能想知道我是如何知道将依赖项添加到“wp-edit-post”脚本在我们的脚本之前。嗯,我不得不深入研究古腾堡的源代码。以下文件<插件文档设置面板>有些不完整,这是古腾堡文献在某些地方缺乏的一个完美例子。

在深入研究代码和浏览文档时,我发现了一些启发性的东西。例如,有两种编写脚本的方法:使用ES5或ESNext语法。ES5不需要构建过程,它引用运行时环境中的代码实例,很可能通过全局水处理变量。例如,创建图标的代码如下所示:

var moreIcon=wp.element.createElement('svg');

ESNext依靠webpack来解决所有依赖关系,这使我们能够导入静态组件。例如,创建图标的代码为:

从“@wordpress/icons”导入{more};

这几乎适用于所有地方。然而,事实并非如此<插件文档设置面板>组件,其中引用ESNext的运行时环境以下为:

const{PluginDocumentSettingsPanel}=wp.editPost;

这就是为什么我们必须向“wp-edit-post”脚本添加依赖项。这就是定义wp.editPost变量的地方。

如果<插件文档设置面板>可以直接导入,然后块编辑器将通过依赖项提取Webpack插件。此插件通过创建一个索引资产.php包含运行时环境脚本的所有依赖项的文件,这些依赖项由更换“@wordpress/”包名称中包含“wp-”因此“@wordpress/edit-post”程序包成为“wp-edit-post”运行时脚本。这就是我如何确定要添加依赖项的脚本。

步骤2:将所有其他CPT上的自定义侧边栏面板列入黑名单

该面板将显示特定CPT的文件,因此必须仅向该CPT注册。这意味着我们需要将其列入黑名单,禁止出现在任何其他帖子类型上。

Ryan Welcher(创建了<插件文档设置面板>组件)描述了此过程注册面板时:

const{registerPlugin}=wp.plugins;const{PluginDocumentSettingsPanel}=wp.editPost(插件文档设置面板)const{withSelect}=wp.data;const MyCustomSideBarPanel=({postType})=>{if('postType-name'!==postType){返回null;  }返回(<插件文档设置面板name=“my-custom-panel”title=“我的自定义面板”    >你好,世界!</PluginDocumentSettingsPanel>  );}const CustomSideBarPanelwithSelect=withSelect(select=>){返回{postType:选择('核心/编辑器').getCurrentPostType(),  };})(MyCustomSideBarPanel);registerPlugin('my-custom-panel',{render:CustomSideBarPanelwithSelect});

他还提出了另一种解决方案,使用使用Select而不是使用Select

这就是说,我并不完全相信这个解决方案,因为JavaScript文件仍然必须加载,即使不需要它,也会迫使网站性能受到影响。这不是更有意义吗注册JavaScript文件,而不是仅仅为了禁用JavaScript而运行JavaScript?

我已经创建了一个PHP解决方案。我承认这感觉有点老套,但效果很好。首先,我们找出与创建或编辑的对象相关的帖子类型:

函数get_editing_post_type():?一串{如果(!is_admin()){返回null;  }全局$pagenow;$typenow=“”;if('post-new.php'===$pagenow){if(isset($_REQUEST['post_type'])&&post_type_exists($_REQUEST['post_type'])){$typenow=$_REQUEST['post_type'];    };}elseif('post.php'===$pagenow){if(isset($_GET['post'])&&isset($_ post['post_ID'])&&(int)$_GET['post']!==(int)$_POST['POST_ID']){//什么都不做}elseif(isset($_GET['post']){$post_id=(int)$_GET[“post”];}elseif(设置($_POST['POST_ID']){$post_id=(int)$_post[“post_id”];    }if($post_id){$post=get_post($post_id);$typenow=$post->post_type;    }  }return$typenow;}

然后,我们仅在脚本与CPT匹配时注册脚本:

add_action('init','maybe_reregister_script');函数可以是_register_script(){//检查这是否是预期的自定义帖子类型if(get_editing_post_type()!='my-custom-post-type'){回报;  }//然后才注册块wp_register_script(…);wp_enqueue_script(…);}

查看此帖子更深入地了解其工作原理。

步骤3:创建自定义指南

我基于WordPress为我的插件指南设计了功能<指南>组件。我一开始没有意识到我会这么做,所以下面是我是如何弄清楚的。

  1. 搜索源代码看看是怎么做到的。
  2. 浏览中所有可用组件的目录古腾堡的故事书

首先,我从块编辑器模态中复制了内容,并进行了基本搜索。结果指给我看这个文件。从那里我发现组件名为<指南>并且可以简单地将其代码复制并粘贴到我的插件中,作为我自己的指南的基础。

然后我查找组件的文档。我浏览了@wordpress/组件包(正如您可能已经猜到的,它是实现组件的地方)并找到组件的README文件。这为我提供了实现自己的自定义指南组件所需的所有信息。

我还浏览了中所有可用组件的目录古腾堡的故事书(这实际上表明这些组件可以在WordPress上下文之外使用)。点击所有这些,我终于发现<指南>故事书提供了几个示例(或故事)的源代码。这是一个方便的资源,用于了解如何通过props自定义组件。

在这一点上,我知道<指南>将为我的组件打下坚实的基础。然而,这里缺少一个元素:如何在点击时触发指南。为了这个我不得不绞尽脑汁!

这是一个带有侦听器的按钮,可在单击时打开模式:

从“@wordpress/element”导入{useState};从“@wordpress/components”导入{Button};从'@wordpress/i18n'导入{__};从“导入MyGuide/指南';const MyGuideWithButton=(道具)=>{const[isOpen,setOpen]=使用状态(false);返回(    <><Button onClick={()=>设置打开(true)}>{__('打开指南:“创建持久查询”')}</按钮>{isOpen(打开)&&(<我的指南{…道具}onFinish={()=>setOpen(false)}        />      ) }    </>  );};导出默认MyGuideWithButton;

即使块编辑器试图隐藏它,我们仍在React中操作。到目前为止,我们一直在处理JSX和组件。但现在我们需要使用状态,这是React特有的。

我想说,如果你想掌握WordPress块编辑器,就必须掌握React。没有办法。

步骤4:向指南添加内容

我们快到了!让我们创建<指南>组件,包含<指南页>每个内容页面的组件。

内容可以使用HTML,包括其他组件等等。在这个特殊的例子中,我添加了三个<指南页>仅使用HTML的CPT实例。第一页包含视频教程,接下来的两页包含详细说明。

从“@wordpress/components”导入{Guide,GuidePage};从'@wordpress/i18n'导入{__};const MyGuide=(道具)=>{返回(<指南{…道具}><指南页><视频宽度=“640”高度=“400”控制><source src=“https://d1c2lqfn9an7pb.cloudfront.net/presentations/graphql-api/videos/graphql-api-creating-persisted-query.mov“type=”视频/mp4“/>{__('您的浏览器不支持视频标记。')}</video(视频)>//等。</GuidePage><指南页>        // ...</GuidePage><指南页>        // ...</GuidePage></指南>}导出默认MyGuide;
图像gif显示了鼠标光标单击块编辑器文档设置中的“打开指南”链接,从而打开自定义欢迎指南,其中包含指向模式中其他页面的链接的视频。
嘿,看,我们现在有自己的向导了!

不错!但也存在一些问题:

  • 我无法将视频嵌入<指南>因为单击播放按钮会关闭指南。我想那是因为<iframe>超出了指南的范围。我最终将视频文件上传到S3并与<视频>
  • 指南中的页面过渡不是很平滑。块编辑器的模式看起来不错,因为所有页面都具有相似的高度,但这一个页面的过渡非常突然。
  • 按钮上的悬停效果可以改进。希望古腾堡团队需要为他们自己的目的修复这个问题,因为我的CSS不在那里。并不是我的技术不好;它们不存在。

但我可以忍受这些问题。就功能而言,我已经完成了我需要的指南。

附加:独立打开文档

对于我们的<指南>,我们创建了每个<指南页>组件直接使用HTML。然而,如果这个HTML代码是通过一个自治组件添加的,那么它可以被重用用于其他用户交互。

例如,组件<缓存控制描述>显示有关HTTP缓存的描述:

const缓存控制描述=()=>{返回(<p>Cache-Control标头将包含请求中涉及的所有字段/指令的最小max-age值,如果max-age为0,则为“no-store”</p>  )}导出默认CacheControlDescription;

此组件可以添加到<指南页>就像我们以前做的那样,但也在<模态>组件:

从“@wordpress/element”导入{useState};从“@wordpress/components”导入{Button};从'@wordpress/i18n'导入{__};从“”导入CacheControlDescription/缓存控制desc’;const CacheControlModalWithButton=(props)=>{const[isOpen,setOpen]=使用状态(false);返回(    <><按钮icon=“编辑帮助”onClick={()=>setOpen(true)}      />{isOpen(打开)&&(<模态{…道具}onRequestClose={()=>setOpen(false)}        ><缓存控制描述/></模式>      ) }    </>  );};导出默认CacheControlModalWithButton;

为了提供良好的用户体验,我们可以仅在用户与块交互时显示文档。为此,我们根据已选择以下为:

从'@wordpress/i18n'导入{__};从“”导入CacheControlModalWithButton/modal-with-btn';const CacheControlHeader=(props)=>{const{isSelected}=道具;返回(    <>{__('缓存-控制最大年龄')}{已选择&&(<CacheControlModalWithButton/>      ) }    </>  );}导出默认CacheControlHeader;

最后<缓存控制标题>组件添加到相应的控件中。

动画gif,显示在编辑器中选择块时查看指南显示的选项。

Tadaaaaaaaa?

WordPress块编辑器是一个相当不错的软件!没有它,我能够完成我无法完成的事情。向用户提供文档可能不是最闪亮的示例或用例,但它非常实用,并且与许多其他插件相关。想把它用于你自己的插件吗?加油!

结账本Cloudways教程有关设置自定义页面模板的详细信息,请参阅。