我正在创建一个WordPress插件,在使用它时会有一个小小的学习曲线。我想给用户一个如何使用插件的入门,但我想避免让用户转向插件网站上的文档,因为这会让他们失去体验。
最好的是用户在安装插件后立即开始使用它,但在积极使用时可以获得有用的提示。WordPress中没有类似的本地功能,但我们可以做一些东西,因为WordPres具有这样的超级灵活性。
这就是我们的想法。我们将把文档直接烘焙到插件中,并使其在块编辑器中易于访问。这样,用户就可以立即使用插件,同时可以直接在他们工作的地方回答常见问题。
我的插件通过几个自定义帖子类型(CPT)进行操作。我们要构建的基本上是用户在使用这些CPT时得到的弹出模式。
WordPress块编辑器是在React中构建的,它使用的组件可以根据不同的情况进行定制和重用。这就是我们正在做的事情-让我们称之为<指南>
组件-它的行为类似于模态,但由几个页面组成,用户可以在其中进行分页。
WordPress本身有一个<指南>
第一次打开块编辑器时显示欢迎指南的组件:
指南是一个装满内容的容器,内容被分解成单独的页面。换句话说,这正是我们想要的。这意味着我们不必重新启动这个项目;我们可以在自己的插件中重用这个相同的概念。
让我们就这样做吧。
我们想要实现的目标
在我们找到解决方案之前,让我们谈谈最终目标。
该设计满足插件的要求,该插件是WordPress的GraphQL服务器。该插件提供了各种CPT,这些CPT通过自定义块进行编辑,而自定义块又是通过模板定义。总共有两个块:一个称为“GraphiQL客户端”,用于输入GraphQL查询,另一个名为“持久化查询选项”,用于自定义执行行为。
由于为GraphQL创建查询不是一项简单的任务,我决定将guide组件添加到该CPT的编辑器屏幕中。它在文档设置中作为一个名为“欢迎指南”的面板提供
打开面板,用户就会得到一个链接。该链接将触发模态。
对于模式本身,我决定在第一页上显示一个关于使用CPT的教程视频,然后在随后的页面上详细描述CPT中可用的所有选项。
我相信这种布局是向用户显示文档的有效方式。这是一个偏僻的地方,但仍然方便地接近行动。当然,我们可以使用不同的设计,甚至可以将模式触发器放置在其他地方,使用不同的组件,而不是重新调整用途<指南>
,但这很好。
规划实施
实施包括以下步骤:
- 构建新脚本以注册自定义侧栏面板
- 在编辑器上仅显示自定义帖子类型的自定义侧栏面板
- 创建指南
- 向指南添加内容
我们开始吧!
步骤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”
运行时脚本。这就是我如何确定要添加依赖项的脚本。
该面板将显示特定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为我的插件指南设计了功能<指南>
组件。我一开始没有意识到我会这么做,所以下面是我是如何弄清楚的。
- 搜索源代码看看是怎么做到的。
- 浏览中所有可用组件的目录古腾堡的故事书。
首先,我从块编辑器模态中复制了内容,并进行了基本搜索。结果指给我看这个文件。从那里我发现组件名为<指南>
并且可以简单地将其代码复制并粘贴到我的插件中,作为我自己的指南的基础。
然后我查找组件的文档。我浏览了@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;
不错!但也存在一些问题:
- 我无法将视频嵌入
<指南>
因为单击播放按钮会关闭指南。我想那是因为<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;
最后<缓存控制标题>
组件添加到相应的控件中。
Tadaaaaaaaa?
WordPress块编辑器是一个相当不错的软件!没有它,我能够完成我无法完成的事情。向用户提供文档可能不是最闪亮的示例或用例,但它非常实用,并且与许多其他插件相关。想把它用于你自己的插件吗?加油!
结账本Cloudways教程有关设置自定义页面模板的详细信息,请参阅。