内容

添加第一个自定义选项 - WordPress Customize API

阅读(13)

这个WordPress Settings API是wordpress自带的为开发者准备的一套API,通过这个,wordpress开发者们可以开发出不同的主题或是插件设置选项,方便您的主题用户自定义主题。那么这样一个东西该如何使用呢?这一节的wordpress开发教程我们就一起来交流下。

介绍:

WordPress定制器允许用户修改和配置主题选项,同时还为他们提供这些更改的实时预览。定制程序旨在为主题开发人员提供一个标准界面,供他们在主题内提供自定义选项时使用。
https://maddisondesigns.com/2017/05/the-wordpress-customizer-a-developers-guide-part-1

首先,你现在一定比较懵,没关系,我们先看看这个所谓的WordPress Settings API可以做哪些事情吧,下面这张图是我制作的一个例子:

WordPress Settings API功能演示

如果你开发的wordpress主题需要的功能不多,那么这些选项足够我们使用了。

正文开始:

我们先明白一些概念,面板、节、设置和控件,他们的关系类似这样:

WordPress Settings API – 面板、节、设置和控件关系图

面板

面板展示在自定义面板的首页,也就是这里:

WordPress Settings API – 面板

那么,什么是节呢?根据上图可知,节在面板下面,如图:

WordPress Settings API – 节

那么设置和控件呢?

设置和控件是在一起的好搭档,可以这么理解,“设置”先挖洞,然后“控件”把“功能”,埋进去,一个“设置”只能挖一个洞,而一个控件只能把一个功能埋进去。这就是设置与控件间的关系了。

设置和控件是在节的下面,而且设置和控件是待在一起的。说了那么多,来实战吧。

实战

以wordpress自带的2019主题为例子,在2019主题根目录下的functions.php文件的<?下面添加以下代码:

//添加自定义设置选项
function npcink_customize_register( $wp_customize ) {
	//给相关设置项目加小铅笔

	if ( isset( $wp_customize->selective_refresh ) ) {

	//One
	$wp_customize->selective_refresh->add_partial( 'npcink_sections_text_one', array(
		'selector'        => '.1-1',
		'render_callback' => 'lifet_customize_partial_npcink_sections_text_one',
	) );

	//Two
		$wp_customize->selective_refresh->add_partial( 'npcink_sections_text_two', array(
			'selector'        => '.1-2',
			'render_callback' => 'lifet_customize_partial_npcink_sections_text_two',
		) );
		
	}
	

	//-----------------------------------------------------------------------------
	//添加主题设置面板,ID = npcink_options
	$wp_customize->add_panel( 'npcink_options',
	array(
		'title' => __( '我是面板 - Npcink', 'npcink' ),
		'description' => __( 'Npcink首发,Muze原创', 'npcink' ),
		'priority'    => 30,
		'capabitity'  => 'edit_theme_options',
	) );

	//添加文本设置节,ID = npcink_sections_text
	$wp_customize->add_section('npcink_sections_text',
	array(                
		'title'     => '节 - Npcink',
		'description' => '主题的文本设置',     
		'panel' => 'npcink_options',
	) );

	//文字设置:one,ID = npcink_sections_text_one
	$wp_customize->add_setting('npcink_sections_text_one',
		array(
			'default' => 'Npcink 荣誉出品',
		)
	);
	$wp_customize->add_control('npcink_sections_text_one',
		array(
			'label' => '文本设置:One',
			'section' => 'npcink_sections_text',
			'type' => 'text',
		)
	);

	//文字设置:two,ID = npcink_sections_text_two
	$wp_customize->add_setting('npcink_sections_text_two',
		array(
			'default' => 'Npcink 荣誉出品',
		)
	);
	$wp_customize->add_control('npcink_sections_text_two',
		array(
			'label' => '文本设置:Two',
			'section' => 'npcink_sections_text',
			'type' => 'text',
		)
	);

}

add_action( 'customize_register', 'npcink_customize_register' );

保存上面的代码,我们就可以在网站后台 → 外观 → 自定义中看到我们添加的新选项:我是面板 – Npcink。

为了方便看到演示效果,我们在主题根目录下的header.php文件底部添加以下代码:

<dl style="padding: 1em;">
    <dd class="1-1">1-1:<?php echo get_theme_mod( 'npcink_sections_text_one', 'Npcink荣誉出品' ); ?></dd>
    <dd class="1-2">1-2:<?php echo get_theme_mod( 'npcink_sections_text_two', 'Npcink荣誉出品' ); ?></dd>
</dl>

保存上面的代码,刷新下自定义,您就可以在自定义中看到这个效果:

WordPress Settings API – 节和控件

到这里为止,您已经知道了WordPress Settings API的面板、节、设置和控件之间的关系,知道了如何跟着教程做出一个设置出来并调用了,那么上面中添加的代码都有啥用呢?迫于篇幅就不在此处提及,请看下文。

怎么添加QQ、微博、Twitter、Facebook的社交分享链接? - Npcink

阅读(15)

当访客看到一篇不错的文章,自然而然的就想收藏或是转发出去,这样一来,就有一点裂变的味道,可以极大的提升你的网站曝光率,如你所见,在一些主流的网站都会在页面中的适当位置添加分享链接。这一节的wordpress开发教程就和大家分享下,怎么添加QQ、微博、Twitter、Facebook的社交分享链接。

实际结合:

结合实际分析,我选取了以下几个主流平台来进行选取:QQ空间、QQ好友、新浪微博、Twitter、Facebook。

需要注意的是,下面提供的方法仅供参考演示,不能直接使用。注意图片防盗链。

方法:

<?php
//社交分享
if ( ! function_exists( 'npcink_post_share' ) ) :
	function npcink_post_share() {
		//获取当前页面链接
		$share_permalink = "https://www.npc.ink/15839.html";
		//当前页面标题
		$share_title = "我是Npcink的页面标题";
		//当前页面分享图片
		$share_img = "互联网图片URL";
		//当前页面的描述信息
		$share_describe = "我是一段Npcink的简单描述";
		//当前页面的分享简述(QQ好友)
		$share_briefly_describe = "我是一段分享Npcink优质内容的简述,仅用在QQ好友中";
?>

<a title="QQ空间" rel="nofollow" target="_blank" href="https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=<?php%20echo%20$share_permalink;%20?>&sharesource=qzone&title=<?php%20echo%20$share_title;%20?>&pics=<?php%20echo%20$share_img;%20?>&summary=<?php%20echo%20$share_describe;%20?>" >QQ空间</a>
<a title="QQ好友" rel="nofollow" target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=<?php%20echo%20$share_permalink;%20?>&sharesource=qzone&title=<?php%20echo%20$share_title;%20?>&pics=<?php%20echo%20$share_img;?>&summary=<?php%20echo%20$share_describe;?>&desc=<?php%20echo%20$share_briefly_describe;?>">QQ好友</a>
<a title="新浪微博" rel="nofollow" target="_blank" href="http://v.t.sina.com.cn/share/share.php?url=<?php%20echo%20$share_permalink;%20?>&title=给你推荐一个很棒的网站:<?php%20echo%20$share_title;%20?>&nbsp;-&content=utf-8">微博</i></a>

<a title="Twitter" rel="nofollow" target="_blank" href="https://twitter.com/intent/tweet?text=给你推荐一个很棒的网站:<?php%20echo%20$share_title;%20?>&nbsp;-&url=<?php%20echo%20$share_permalink;%20?>">Twitter</a>
<a title="Facebook" rel="nofollow" target="_blank" href="http://www.facebook.com/share.php?u=<?php%20echo%20$share_permalink;%20?>&t=<?php%20echo%20$share_title;%20?>">Facebook</i></a>

<?php
	}
endif;

调用:

<?php npcink_post_share(); //社交分享?>

实际效果:

QQ空间
QQ好友
微博
Twitter
Facebook

02:讲透WordPress 菜单开发 - 权限控制与加载指定JS

阅读(24)

承接上文,本节主要解决两个问题,

  • 菜单权限控制 – 仅限访问指定菜单和仅限指定人员访问菜单
  • 指定菜单页加载 CSS 和 JS

问题

本节围绕以下两个常见问题进行展开

权限控制

虽然默认的权限控制已经好用,但有时,我们希望只有指定 ID 的用户才能访问菜单。

比如,在我开发退款插件时,为了控制退款员工的权限,我只让他们访问 退款 菜单,其他菜单都拒绝访问,避免造成其他影响。

我可不希望退款员工删除网站首页,那就糟糕了
但是,只有管理员才有退款权限

加载 JS 和 CSS

默认的加载方式是在所有菜单页面上都加载 JS 和 CSS 资源,当加载的资源过多,会造成所有菜单页都会卡顿。

这点太不好了,该省省,该花花,这点浏览器资源,还是很有必要节省的。

通过在指定的菜单上才加载资源,可以有效的节省浏览器资源,提升后台页面访问速度。

后台一般是给管理者使用的,一般不太在乎页面加载速度,但是,这个是可以省的,我们还是节省下吧

流程

仅限指定用户可访问此菜单

获取用户ID

一般用户 ID 是设置选项给出的一个数组,这里我们进行简化,直接从用户列表中拿几个用户 ID 组成数组。

在用户列表中,选择你需要获取的用户,鼠标点击编辑按钮,

在 URL 中,注意这个 user_id 字段

  1. http://localhost:10004/wp-admin/user-edit.php?user_id=2&wp_http_referer=%2Fwp-admin%2Fusers.php

其中,user_id 后面的值,就是用户 ID 了,我们凭借这个值,来判断当前访问的用户。

我们将需要控制的用户 ID 组成数组

  1. $a = array(1,2,3,4,5,6);// 示例数组,存储若干用户ID

添加判断

还记得之前讲到,是如何创建顶级菜单的嘛?实例代码如下

  1. //创建一个菜单
  2. function vuespa_create_menu_page()
  3. {
  4. add_menu_page(
  5. 'VueSpa选项',// 此菜单对应页面上显示的标题
  6. 'VueSpa',// 要为此实际菜单项显示的文本
  7. 'administrator',// 哪种类型的用户可以看到此菜单
  8. 'vuespa_id',// 此菜单项的唯一ID(即段塞)
  9. 'vuespa_menu_page_display',// 呈现此页面的菜单时要调用的函数的名称
  10. 'dashicons-admin-customizer',//图标 - 默认图标
  11. '500.1',//位置
  12. );
  13. }// end vuespa_create_menu_page
  14. add_action('admin_menu','vuespa_create_menu_page');

我们对其添加一个判断,若当前访问菜单的人是指定的用户 ID,则输出空值,不显示菜单。

判断条件如下:

  1. if(
  2. current_user_can('administrator')
  3. && is_array($a)
  4. && in_array(get_current_user_id(), $a)
  5. ){}
  • 判断是否拥有管理员权限(可按需设置)
  • 并且,变量 $a 是否是数组
  • 并且,当前访问菜单的用户 ID 是否在数组 $a 中。

只有当前访问菜单的用户 ID 符合以上所有需求,才会展示菜单。

is_array() 用于类型检查,提升代码健壮性
示例

  1. //创建一个菜单
  2. function vuespa_create_menu_page()
  3. {
  4. //控制权限的员工 ID
  5. $a = array(1,2,3,4,5,6);// 示例数组,存储若干用户ID
  6. //权限判断
  7. if(
  8. current_user_can('administrator')
  9. && is_array($a)
  10. && in_array(get_current_user_id(), $a)
  11. ){
  12. add_menu_page(
  13. 'VueSpa选项',// 此菜单对应页面上显示的标题
  14. 'VueSpa',// 要为此实际菜单项显示的文本
  15. 'administrator',// 哪种类型的用户可以看到此菜单
  16. 'vuespa_id',// 此菜单项的唯一ID(即段塞)
  17. 'vuespa_menu_page_display',// 呈现此页面的菜单时要调用的函数的名称
  18. 'dashicons-admin-customizer',//图标 - 默认图标
  19. '500.1',//位置
  20. );
  21. }
  22. }// end vuespa_create_menu_page
  23. add_action('admin_menu','vuespa_create_menu_page');

现在,我们开发的这个菜单,即使是管理员来了,也只有指定的管理员才能访问。

指定用户仅限访问指定菜单

业务介绍:

在开发退款插件时,只有管理员才有退款权限,所以,只能给退款员工以管理员权限。

但是为了防止退款员工进行除退款外的其他操作,我们需要限制退款员工只能访问退款菜单。

流程

  1. 提供的变量是否是数组
  2. 当前访问的用户ID是否在提供数组中
  3. 获取当前链接的 page 字段,是否是指定菜单
  4. 如果是 admin-ajax.php 或 admin-post.php,则不拦截
  5. 符合以上全部信息,则拦截,并提供提示信息

对于第四点,如果我们当前在限制的菜单中,当我们要进行数据查询或保存数据等操作,会通过以上两个文件发出请求,这会因为不符合第3个条件而被拦截,这里做个例外。

示例代码

以下代码可供参考

  1. //权限管理
  2. add_action('admin_init','mqzj_restrict_access');
  3. function mqzj_restrict_access()
  4. {
  5. $user = wp_get_current_user();
  6. $a = array(1,2,3,4,5,6);// 示例数组,存储若干用户ID
  7. // 如果 $a 为空或为字符串,则将其赋值为空数组
  8. if(empty($a)|| is_string($a)){
  9. $a = array();
  10. }
  11. //是限定 ID
  12. if(in_array($user->ID, $a)){
  13. //在访问限定菜单
  14. if((isset($_GET['page'])&& $_GET['page']=='refund_querys')||(isset($_GET['page'])&& $_GET['page']=='npcink_orders_list')){
  15. return;
  16. } elseif (
  17. // 如果是 admin-ajax.php 或 admin-post.php,则不拦截
  18. preg_match('/^/wp-admin/(admin-ajax.php|admin-post.php)/', $_SERVER['PHP_SELF'])){
  19. return;
  20. }else{
  21. //跳转
  22. $adminPage = get_admin_url().'admin.php';
  23. $refundPage = get_admin_url().'index.php';
  24. $message ='
  25. 您暂无授权访问此页面,请联系管理员授权!
  26. <ul>
  27. <li>
  28. <a href="'.%20$adminPage%20.'?page=npcink_orders_list">订单管理</a>
  29. </li>
  30. <li>
  31. <a href="'.%20$refundPage%20.'?page=refund_querys">订单退款</a>
  32. </li>
  33. </ul>
  34. ';
  35. wp_die($message);
  36. exit;
  37. }
  38. }
  39. }

上面仅允许访问和提示的菜单如下

  1. https://www.npc.ink/wp-admin/index.php?page=refund_querys
  2. https://www.npc.ink/wp-admin/admin.php?page=npcink_orders_list&order_state=f

相信你已经掌握了其中的思路,留一个思考题,

你知道如何禁止访问评论菜单,比如这样的链接

  1. https://www.npc.ink/wp-admin/edit-comments.php

欢迎您在评论区中给出答案。

控制 JS 和 CSS 在菜单中的加载

加载资源

我们一般加载上述资源是这样写的

  1. function vuespa_load_vues($hook)
  2. {
  3. //版本号
  4. $ver ='66';
  5. //加载到页面顶部
  6. wp_enqueue_style('vite', plugin_dir_url(__FILE__).'vites/dist/index.css', array(), $ver,false);
  7. //加载到页面底部
  8. wp_enqueue_script('vite', plugin_dir_url(__FILE__).'vites/dist/index.js', array(), $ver,true);
  9. }
  10. //样式加载到后台
  11. add_action('admin_enqueue_scripts','vuespa_load_vues');

这样,我们就在所有后台的所有页面上,加载了上述资源

获取 $hook

还记得之前创建菜单时,每个菜单都有唯一的 slug 嘛?每个菜单页面也是有唯一的 $hook 的。

我们可以通过以下代码获取当前菜单的 $hook 值。

  1. function wpdocs_myselective_css_or_js( $hook ){
  2. echo '<h1 style="color: crimson;text-align: center;">'. esc_html( $hook ).'</h1>';
  3. }
  4. add_action('admin_enqueue_scripts','wpdocs_myselective_css_or_js');

打开我们需要获取的菜单页面,效果如下

进行判断

有了唯一值,剩下的交给判断就好了。

  1. function vuespa_load_vues($hook)
  2. {
  3. //判断当前页面是否是指定页面,是则继续加载
  4. if('toplevel_page_vuespa_id'!= $hook){
  5. return;
  6. }
  7. //版本号
  8. $ver ='53';
  9. //加载到页面顶部
  10. wp_enqueue_style('vite', plugin_dir_url(__FILE__).'vites/dist/index.css', array(), $ver,false);
  11. //加载到页面底部
  12. wp_enqueue_script('vite', plugin_dir_url(__FILE__).'vites/dist/index.js', array(), $ver,true);
  13. }
  14. //样式加载到后台
  15. add_action('admin_enqueue_scripts','vuespa_load_vues');

通过上述代码中的 if 判断,就只会在符合要求的 $hook 菜单上加载资源了。

总结

本节关注菜单权限方面,进行了菜单展示权限和菜单中加载资源的研究,这是我们日常开发中常用的功能。

通过上述两个权限的适当配合,可以一定程度上解决部分安全问题和页面加载缓慢的问题。

关于 WordPress 菜单的主要内容,到此为止,后续会根据实际问题,不定期更新相关内容。

01:讲透WordPress 菜单开发 - 在菜单中添加Tab选项卡

阅读(13)

在上一节中,我们学习了如何在 WordPress 中添加菜单的操作,包括顶级菜单、子菜单、默认菜单下的子菜单等。

相信你已经可以按照自己的实际需求添加菜单了。

本文主要解决如何在菜单中添加 Tab 选项卡的问题。

问题详解

若单一菜单中内容过多,加子菜单又嫌麻烦,可尝试添加本节介绍的 Tab 选项卡菜单。

他可以在当前页面中展示多个选项卡,可以很方便的在当前菜单页面中的各个 Tab 选项卡中进行切换,效果如下:

流程和帮助

流程如下

感谢以下文章的帮助

  • WordPress 设置 API,第 5 部分:设置的选项卡式导航 (tutsplus.com)

添加菜单

根据上一节的内容,我们先添加一个顶级菜单,在当前使用主题的 functions.php 文件底部添加以下内容

  1. //创建一个Demo菜单
  2. function demo_create_menu_page()
  3. {
  4. add_menu_page(
  5. 'Demo选项',// 此菜单对应页面上显示的标题
  6. 'Demo',// 要为此实际菜单项显示的文本
  7. 'administrator',// 哪种类型的用户可以看到此菜单
  8. 'demo_id',// 此菜单项的唯一ID(即段塞)
  9. 'demo_menu_page_display',// 呈现此页面的菜单时要调用的函数的名称
  10. 'dashicons-smiley',//图标 - 默认图标
  11. '600.1',//位置
  12. );
  13. }// end vuespa_create_menu_page
  14. add_action('admin_menu','demo_create_menu_page');
  15. //Demo菜单的回调
  16. function demo_menu_page_display()
  17. {
  18. ?>
  19. <div class="wrap">
  20. Npcink
  21. </div><!--/wrap-->
  22. <?php
  23. }

接下来的内容,就是围绕这个菜单回调函数 demo_menu_page_display() 来操作的。

添加选项卡

WordPress 准备好了一套默认的样式,我们直接使用即可。在回调函数中添加以下内容

  1. <h2class="nav-tab-wrapper">
  2. <ahref="?page=demo_id&tab=display_options"class="nav-tab">Display Options</a>
  3. <ahref="?page=demo_id&tab=social_options"class="nav-tab">Social Options</a>
  4. </h2>

这里,我们手动构造了两个选项卡和选项卡的链接,通过点击不同的选项卡,触发不同的链接。

注意 demo_id 部分,这里是用的顶级菜单的slug 的,需要注意凭借这个,才能找到我们需要的内容

菜单切换

为了知道当前是哪个选项卡,我们需要添加以下函数

  1. <?php
  2. if( isset( $_GET['tab'])){
  3. $active_tab = $_GET['tab'];
  4. }// end if
  5. ?>

编写一个条件来检查是否设置了查询字符串值,如果是,则将其存储在变量中。

然后,为了让用户知道当前所在的菜单,我们需要进行判断,

  • 若当前的 Tab 的值 是当前菜单,显示选中状态。
  • 若当前的 Tab 的值 不是当前菜单,显示未选中状态。

修改上面的选项卡内容

  1. <h2class="nav-tab-wrapper">
  2. <a href="?page=demo_id&tab=display_options" class="nav-tab <?php echo $active_tab =='display_options'?'nav-tab-active':'';?>">Display Options</a>
  3. <a href="?page=demo_id&tab=social_options" class="nav-tab <?php echo $active_tab =='social_options'?'nav-tab-active':'';?>">Social Options</a>
  4. </h2>

WordPress 也提供了对应的样式,这里,我们通过三元运算符实现样式切换。

若当前链接显示的是当前菜单选项,则添加样式 nav-tab-active

然后,初次进入菜单时,我们是拿不到 Tab 选项内容的,此时,用户还没有点击我们构造的链接。

这里需要设置一个默认展示的菜单。

  1. $active_tab = isset( $_GET['tab'])? $_GET['tab']:'display_options';

这样,在初次进入菜单页面时,如果拿不到 Tab 的值,就展示菜单 display_options 的值。

内容切换

完成了菜单切换,我们还需要切换内容的显示,不同的菜单对应不同的内容。

我们添加以下代码

  1. <?php
  2. //根据当前 Tab 展示对应内容
  3. if($active_tab =='display_options'){
  4. ?>
  5. <h3>Npcink 放置需要展示的内容</h3>
  6. <?php
  7. }else{
  8. ?>
  9. <h3>Npcink 放置准备展示的函数</h3>
  10. <?php
  11. //展示设置字段和设置节
  12. }// end if/else
  13. ?>

通过简单的 if 判断,就能根据不同的菜单 Tab 展示不同的内容了。

当然,若您有兴趣了解的话,可以查查什么是设置字段和设置节,

一般情况下,这块的内容应该是这样的

  1. <formmethod="post"action="options.php">
  2. <?php
  3. //根据当前 Tab 展示对应内容
  4. if($active_tab =='display_options'){
  5. ?>
  6. <h3>Npcink 放置需要展示的内容</h3>
  7. <?php
  8. //展示设置字段和设置节
  9. settings_fields('sandbox_theme_display_options');
  10. do_settings_sections('sandbox_theme_display_options');
  11. }else{
  12. ?>
  13. <h3>Npcink 放置准备展示的函数</h3>
  14. <?php
  15. //展示设置字段和设置节
  16. settings_fields('sandbox_theme_social_options');
  17. do_settings_sections('sandbox_theme_social_options');
  18. }// end if/else
  19. //保存按钮
  20. submit_button();
  21. ?>
  22. </form>

完整代码

前面详细介绍了每一步的作用,这里给出完整代码,供大家参考

  1. //创建一个Demo菜单
  2. function demo_create_menu_page()
  3. {
  4. add_menu_page(
  5. 'Demo选项',// 此菜单对应页面上显示的标题
  6. 'Demo',// 要为此实际菜单项显示的文本
  7. 'administrator',// 哪种类型的用户可以看到此菜单
  8. 'demo_id',// 此菜单项的唯一ID(即段塞)
  9. 'demo_menu_page_display',// 呈现此页面的菜单时要调用的函数的名称
  10. 'dashicons-smiley',//图标 - 默认图标
  11. '600.1',//位置
  12. );
  13. }// end vuespa_create_menu_page
  14. add_action('admin_menu','demo_create_menu_page');
  15. //Demo菜单的回调
  16. function demo_menu_page_display()
  17. {
  18. ?>
  19. <div class="wrap">
  20. <!--标题-->
  21. <h2>
  22. <?php echo esc_html(get_admin_page_title());?>
  23. </h2>
  24. <!--在保存设置时调用WordPress函数以呈现错误.-->
  25. <?php settings_errors();?>
  26. <?php
  27. //检查URL中是否存在名为 "tab" 的GET参数,并将其值分配给变量 $active_tab
  28. if(isset($_GET['tab'])){
  29. $active_tab = $_GET['tab'];
  30. }// end if
  31. //设置默认值
  32. $active_tab = isset($_GET['tab'])? $_GET['tab']:'display_options';
  33. ?>
  34. <!--这里的链接手动构造,注意,page=你填的菜单slug-->
  35. <h2 class="nav-tab-wrapper">
  36. <a href="?page=demo_id&tab=display_options"class="nav-tab <?php echo $active_tab == 'display_options' ? 'nav-tab-active' : ''; ?>">DisplayOptions</a>
  37. <a href="?page=demo_id&tab=social_options"class="nav-tab <?php echo $active_tab == 'social_options' ? 'nav-tab-active' : ''; ?>">SocialOptions</a>
  38. </h2>
  39. <form method="post" action="options.php">
  40. <?php
  41. //根据当前 Tab 展示对应内容
  42. if($active_tab =='display_options'){
  43. ?>
  44. <h3>Npcink放置需要展示的内容</h3>
  45. <?php
  46. //展示设置字段和设置节
  47. settings_fields('sandbox_theme_display_options');
  48. do_settings_sections('sandbox_theme_display_options');
  49. }else{
  50. ?>
  51. <h3>Npcink放置准备展示的函数</h3>
  52. <?php
  53. //展示设置字段和设置节
  54. settings_fields('sandbox_theme_social_options');
  55. do_settings_sections('sandbox_theme_social_options');
  56. }// end if/else
  57. //保存按钮
  58. submit_button();
  59. ?>
  60. </form>
  61. </div><!--/wrap-->
  62. <?php
  63. }

注意,因为我们没有配置设置字段和设置节,所以,点击保存按钮会报错,

为了专注,这里仅做拓展介绍,并不提供相关实现内容。

第零节:Vue3 开发WordPress设置选项 - 效果预览与使用场景

阅读(8)

开发 WordPress 的设置功能时,只需要几个简单的设置选项,用原生Setting API太麻烦,用设置框架又太重,也许这个对前端友好的不轻不重的方法会适合你。

问题

Npcink 想要在 WordPress 平台开发一款支持微信和支付宝的退款插件,他只需要6个输入选项和一个筛选选项。

  • 3个输入框用来填支付宝配置
  • 3个输入框用来填微信配置
  • 一个下列筛选用来选择有退款权限的客服

Setting API

首先,他想到使用 WordPress 原生的 setting APi 来实现,于是,他写下了 PHP 与 HTML 混合的代码,其中一段类似这样

<tr>
 <th scope="row"><label for="<?php echo $option_name_1; ?>">微信支付商户号</label></th>
 <td><input type="text" id="<?php echo $option_name_1; ?>" name="<?php echo $option_name_1; ?>" value="<?php echo esc_attr( $value_1 ); ?>"></td>
</tr>

啊,头皮发麻,他本就不够用的头发又稀疏了不少。而且,自己还要负责写功能、写验证、写外观,碰到了不同类型的输入还得重来一次。

于是,当他聪明的用循环写完了两个tab选项下的三个输入框后,随着需求,他需要再添加一个下拉筛选框时,他陷入了深深的思考中。

本来是为了加快开发速度,想着选项比较少,就用原生设置来做的,没想到吃力不讨好,加个小需求就难住了。

设置框架

还好,开源世界中,已有大佬开发了框架,直接拿来用就行。例如有以下几个常用选择

  1. Kirk3(小部件设置 – 主题常用)
  2. options framework(小插件,小主题在用)
  3. OptionTree – WordPress 的主题选项 UI Builder — SitePoint
  4. CODESTAR 框架(主流主题在用)
  5. CMB2
  6. ACF

只要理解他们的文档,遵循他们的方法,就能使用这些框架提供的强大能力了,开发插件还不是手到擒来。

但是我只想添加6个输入框和一个下列筛选框啊,其他功能我也用不上。

我拿着倚天剑不去称霸武林,去串肉串搞烧烤嘛,

……

第三种选择

WordPress 现在已经支持 REST API了,那我自己弄套前端,把数据通过 REST API提供给 WordPress 就好啦。

之前,Npcink 就已经使用 vue3 开发了一些简单的SPA单页,技术还是够用的。

效果预览

先看下最终效果

可以理解为一个简单的前后端分离的小项目,前端使用 vue3 和 Element Plus 给出,通过 Axios 与后端的 WordPress 的 REST API 进行通信,再让后端通过get_option函数拿到设置选项,进行功能设置。

技术流程

使用场景

适合那些有一些设置,但又不多的主题或插件,

  • 用原生 Setting API 太麻烦,
  • 用设置框架又多余

当然,如果你是前端写后端的,就更适合了,甚至你可以将所有选项都通过此方法来实现,说不定还更顺手。

解决痛点

  • 前后端分离,展示与功能分开,方便维护
  • 更适合前端的设置方法
  • 后端提供两个接口,前端一顿调用就行

后续文章持续撰写中,点个关注,获取平台最新文章推送。

第六节:Vue3 开发WordPress设置选项 - 添加对象传值和数据校验

阅读(12)

承接上一节,我们使用 Vite 对JS资源进行打包后,优化了不少性能,但也有不少麻烦还没有解决,这次来做个收尾。

  • 本系列代码分享在 GitHub 中,希望能帮助大家理解
  • github.com/muze-page/vu

流程

多对象传值

在上一节的 Option.vue 文件中,有这样的代码

  1. const datas = reactive({
  2. dataOne:"",
  3. dataTwo:"",
  4. dataName:[],
  5. dataImage:"",
  6. dataSelectedImage:"",
  7. });

看起来没啥问题,但我现在需要开发2个 Tab 选项,每个选项中都有3个选项需要填写,类似这样

  1. let datas = reactive({
  2. npc_zfb_appid:"",
  3. npc_zfb_private_key:"",
  4. npc_zfb_public_key:"",
  5. npc_wx_mch_id:"",
  6. npc_wx_cert_api:"",
  7. npc_wx_cert_key:"",
  8. });

嗯,还是没啥问题,但是,我要是有10个 Tab 选项呢?

如果你只是低头跑,你总会撞上山的

聪明的你想到了,可以这样啊

  1. let datas = reactive({
  2. zfb:{
  3. npc_zfb_appid:"",
  4. npc_zfb_private_key:"",
  5. npc_zfb_public_key:"",
  6. },
  7. wx:{
  8. npc_wx_mch_id:"",
  9. npc_wx_cert_api:"",
  10. npc_wx_cert_key:"",
  11. },
  12. npc_refund_user:[],
  13. });

有其他需求再接着分,或者分了再分。

这当然没问题,但我们的获取选项的接口就不够用了,
需要修改 interface.php 文件中的函数 get_option_by_RestAPI() 为以下内容

  1. //读取Option
  2. //支持数组类数据请求
  3. function get_option_by_RestAPI($data)
  4. {
  5. // 将输入数据转换成数组类型
  6. $dataArray = json_decode($data->get_body(),true);
  7. $return = array();
  8. // 遍历数组,检查每个元素是否为对象
  9. foreach($dataArray as $option_name => $value){
  10. // 初始化当前选项的值数组
  11. $option_value = array();
  12. // 如果当前元素是一个非空数组,则遍历其中的每个字段
  13. if(is_array($value)&&!empty($value)){
  14. foreach($value as $field_name => $field_value){
  15. // 获取指定选项的值,如果不存在,则使用空字符串代替
  16. $option_value[$field_name]= get_option($field_name,'');
  17. }
  18. // 将当前选项及其值添加到返回数组中
  19. $return[$option_name]= $option_value;
  20. }else{
  21. // 如果当前元素非数组或数组为空,获取指定选项的值
  22. $return[$option_name]= get_option($option_name,'');
  23. }
  24. }
  25. return $return;// 返回所有选项的键值对
  26. }

这里面进行了若干次判断,并进行对应的处理,以保证我们可以正常是使用上面提到的数据结构。

浏览器的响应结构就是这样的

数据校验

如果我们需要的是用户名,但使用者却填写了手机号,这时,会导致拿不到需要的数据,就需要在数据提交前进行数据校验,来保证数据的正确性。

代码的使用者是一匹野马,你不能指望他按照你的想法去跨红色的栏

为了便于演示,我们使用方便的正则来进行数据校验。分别是姓名和手机号

准备数据

我们修改 Option.vue 文件,准备两个变量用来存储我们的选项值

  1. //存储选项值
  2. const datas = reactive({
  3. //省略
  4. check:{
  5. name:"",
  6. phone:"",
  7. },
  8. });

这里,我使用了结构化的数据,便于数据管理。

数据获取里也得加上

  1. //获取数据
  2. const get_option =()=>{
  3. axios
  4. .post(dataLocal.route +"pf/v1/get_option", datas,{
  5. headers:{
  6. "X-WP-Nonce": dataLocal.nonce,
  7. "Content-Type":"application/json",
  8. },
  9. })
  10. .then((response)=>{
  11. //省略
  12. datas.check.name = data.check.name;
  13. datas.check.phone = data.check.phone;
  14. })
  15. .catch((error)=>{
  16. window.alert("连接服务器失败或后台读取出错!数据读取失败");
  17. console.log(error);
  18. });
  19. };

这样,就能在页面初始加载时,拿到选项的默认值了

准备正则

我们准备两个计算属性,通过正则来判断,并输出对应的布尔值

  1. //验证名称
  2. const isName = computed(()=>{
  3. // 正则表达式验证名字,2到6个中文字符
  4. const reg =/^[u4e00-u9fa5]{2,6}$/;
  5. return reg.test(datas.check.name);
  6. });
  7. //验证电话号码
  8. const isPhone = computed(()=>{
  9. // 正则表达式验证电话号码
  10. const reg =/^1[3456789]d{9}$/;
  11. return reg.test(datas.check.phone);
  12. });

他会持续的拿到输入的值,并进行判断输出的。

准备模版

我们在模版文件中,将对应的值用上

  1. <h3>数据校验</h3>
  2. 姓名:<inputtype="text"v-model="datas.check.name"/>
  3. <pv-if="!isName"class="check">格式错误 - 必须为两字到六字中文</p>
  4. <br/>
  5. 手机号:<inputtype="text"v-model="datas.check.phone"/>
  6. <pv-if="!isPhone"class="check">格式错误 - 必须是1开头的11位手机号</p>
  7. <hr/>

这样,当拿到的值符合正则需求时,计算函数输出 true ,然后 if 反向判断为 false ,不显示警告内容。

效果

执行打包命令后,刷新 VueSpa 菜单可查看效果

提示信息的外观,我加了点小小的样式。

还有以下几点需要改进

  • 点击保存按钮进行数据校验
  • 校验通过才能正常报存
  • 将校验数据模块化,可复用

当然,基于 Node 生态,您还可以通过诸多现成的校验框架更方便的实现同样的功能。

总结

现在,前后端进行了分离,后端只需负责存储数据,前端负责准备和展示数据即可。大大提升了代码的可维护性和健壮性,降低了浏览器的性能开销。

当然,在前端工程化的路上,你还有无限多的可能,待您探索。

本系列章节核心内容到此为止,后续有更棒的想法,会持续分享给大家的。

第五节:Vue3 开发WordPress设置选项 - 使用Vite打包JS资源

阅读(57)

承接上文,我们将常用选项中的两个功能给实现了,这一节,我们将对 JS 资源进行打包,最终产物只有一个 JS 文件和一个 CSS 文件,可以极大的提升网页加载速度,减少依赖。

打包后的文件中,就包含了 vue3 和 Axios 的代码内容,不必通过URL 引入 vue3 的所有功能了。而且,在打包之前,还能对各种选项进行自定义的验证,还能使用各种前端框架,对选项进行美化。

更重要的是,使用打包后的文件,可以明显减少页面卡顿。

  • 本系列代码分享在 GitHub 中,希望能帮助大家理解
  • github.com/muze-page/vu

本节流程

准备打包环境

  • Node.js 安装配置 | 菜鸟教程 (runoob.com)

我们使用 Vite 进行打包,您需要提前安装 node 环境,

创建环境

使用 VS Code 打开我们的vue-spa文件夹,通过Ctrl+~ 打开终端,输入以下命令创建 Vue 3 项目

  1. npm create vite@latest vites ----template vue

稍等片刻后,分别执行以下几个命令

  1. cd vites
  2. npm install
  3. npm run dev

会提示一个网址,

我们将其在浏览器中打开,即可看到如下显示

我们同时按下 Ctrl+c 暂停当前运行,回到控制台中。

安装Axios

在控制台中输入以下命令,安装 Axios

  1. npm install axios

安装完成后,打开vites文件夹下的 package.json 文件即可看到如下提示

即代表您安装成功了。

重写index.js

现在,我们需要重写 index.js 文件,我们在 vites/src/components/ 文件夹下,新建文件 Option.vue 文件。写入以下内容

  1. <scriptsetup>
  2. import{ reactive, onMounted } from "vue";
  3. import axios from "axios";
  4. const siteData = dataLocal.data;
  5. //存储获取的值
  6. const getData = reactive({
  7. //存储获取的媒体库值
  8. mediaList:[],
  9. });
  10. //存储选项值
  11. const datas = reactive({
  12. dataOne:"",
  13. dataTwo:"",
  14. dataName:[],
  15. dataImage:"",
  16. dataSelectedImage:"",
  17. });
  18. //获取数据
  19. const get_option =()=>{
  20. axios
  21. .post(dataLocal.route +"pf/v1/get_option", datas,{
  22. headers:{
  23. "X-WP-Nonce": dataLocal.nonce,
  24. "Content-Type":"application/json",
  25. },
  26. })
  27. .then((response)=>{
  28. const data = response.data;
  29. datas.dataOne = data.dataOne;
  30. datas.dataTwo = data.dataTwo;
  31. datas.dataName = data.dataName;
  32. datas.dataImage = data.dataImage;
  33. datas.dataSelectedImage = data.dataSelectedImage;
  34. })
  35. .catch((error)=>{
  36. window.alert("连接服务器失败或后台读取出错!数据读取失败");
  37. console.log(error);
  38. });
  39. };
  40. //保存数据
  41. const update_option =()=>{
  42. console.log(datas);
  43. axios
  44. .post(dataLocal.route +"pf/v1/update_option", datas,{
  45. headers:{
  46. "X-WP-Nonce": dataLocal.nonce,
  47. },
  48. })
  49. .then((response)=>{
  50. alert("保存成功");
  51. })
  52. .catch((error)=>{
  53. alert("保存失败");
  54. console.log(error);
  55. });
  56. };
  57. //上传图片
  58. const upload_img =(file)=>{
  59. const formData =newFormData();
  60. formData.append("file", file);
  61. return axios
  62. .post(dataLocal.route +"wp/v2/media", formData,{
  63. headers:{
  64. "X-WP-Nonce": dataLocal.nonce,
  65. "Content-Type":"multipart/form-data",
  66. },
  67. })
  68. .then((response)=>{
  69. // 图片上传成功后的处理逻辑
  70. const data = response.data;
  71. //返回图片URL
  72. return data.source_url;
  73. })
  74. .catch((error)=>{
  75. console.error(error);
  76. // 图片上传失败后的处理逻辑
  77. });
  78. };
  79. //处理图片上传事件
  80. const update_img =(event)=>{
  81. const file = event.target.files[0];
  82. upload_img(file).then((url)=>{
  83. //将拿到的图片URL传给图片变量
  84. datas.dataImage = url;
  85. });
  86. };
  87. //清空选择图片
  88. const clear_img =()=>{
  89. datas.dataImage ="";
  90. };
  91. //获取媒体库图片
  92. const getMediaList =()=>{
  93. axios
  94. .get(dataLocal.route +"wp/v2/media")
  95. .then((response)=>{
  96. getData.mediaList = response.data;
  97. })
  98. .catch((error)=>{
  99. console.error(error);
  100. });
  101. };
  102. //从媒体库选中图片
  103. const selectImage =(imageUrl)=>{
  104. datas.dataSelectedImage = imageUrl;
  105. };
  106. //页面初始加载
  107. onMounted(()=>{
  108. //获取选项值
  109. get_option();
  110. });
  111. </script>
  112. <template>
  113. <!--两个输入框-->
  114. 文本框1:<inputtype="text"v-model="datas.dataOne"/><br/>
  115. 文本框2:<inputtype="text"v-model="datas.dataTwo"/>
  116. <hr/>
  117. <!--用户选择-->
  118. 用户选择:<selectv-model="datas.dataName"multiple>
  119. <optionv-for="option in siteData.user" :key="option.id" :value="option.id">
  120. {{ option.name }}
  121. </option>
  122. </select>
  123. <p>你选择了:{{ datas.dataName }}</p>
  124. <br/>
  125. 按住command(control)按键即可进行多选
  126. <hr/>
  127. <!--图片上传-->
  128. <inputtype="file" @change.native="update_img"/><br/>
  129. <buttontype="button" @click="clear_img">清理</button><br/>
  130. <img :src="datas.dataImage"v-if="datas.dataImage"/>
  131. <hr/>
  132. <!--获取媒体库图片-->
  133. <button @click="getMediaList">获取媒体库图片</button>
  134. <divclass="box">
  135. <divv-for="media in getData.mediaList" :key="media.id"style="float: left">
  136. <img :src="media.source_url"/>
  137. <button @click="selectImage(media.source_url)">选择</button>
  138. </div>
  139. </div>
  140. <h2>{{ datas.dataSelectedImage ? "已" : "未" }}选择图片</h2>
  141. <img :src="datas.dataSelectedImage"v-if="datas.dataSelectedImage"/>
  142. <hr/>
  143. <buttonclass="button button-primary" @click="update_option">保存</button>
  144. </template>
  145. <stylescoped>
  146. img {
  147. max-width:150px;
  148. height:auto;
  149. vertical-align: top;
  150. }
  151. .box {
  152. max-width:800px;
  153. display: flex;
  154. margin:1em0;
  155. }
  156. </style>

这里,对原有写法在语法糖 setup 的帮助下,进行了部分重写,再抽离了部分CSS样式,使得整体的代码更加健壮和容易维护了。

当然,还有更多方法可以优化,为了便于讲解,这里不再赘述。

修改 App.js

/vites/src/

模块制作好了,我们在 App.vue 文件中导入,写入以下内容

  1. <scriptsetup>
  2. //import HelloWorld from "./components/HelloWorld.vue";
  3. importOption from "./components/Option.vue";
  4. </script>
  5. <template>
  6. <Option></Option>
  7. </template>
  8. <stylescoped></style>

将我们写的组件展示出来

修改main.js

/vites/src/

在之前的章节中,我们提前准备的ID 是 vuespa ,所以,需要修改下此文件为以下内容

  1. import{ createApp }from'vue'
  2. //import './style.css'
  3. importAppfrom'./App.vue'
  4. createApp(App).mount('#vuespa')

在这里,我还把默认的 CSS 样式给注释了

修改 vite.config.js

/vites/src/

为了让打包后的文件名与我们原有的文件名保持一致,我们需要修改下打包细节,替换该文件为以下内容

  1. import{ defineConfig }from"vite";
  2. import vue from"@vitejs/plugin-vue";
  3. // https://vitejs.dev/config/
  4. exportdefault defineConfig({
  5. plugins:[vue()],
  6. build:{
  7. rollupOptions:{
  8. output:{
  9. // 指定 chunk 文件名(含导出的代码)
  10. //chunkFileNames: 'js/[name].js',
  11. // 指定静态资源文件名(不含导出的代码)
  12. //assetFileNames: 'assets/[name].[ext]',
  13. entryFileNames:"index.js",
  14. assetFileNames:"[name][extname]",
  15. chunkFileNames:"[name].js",
  16. },
  17. },
  18. },
  19. });

这样,打包后就会产出 index.js 和 index.css 文件了,而不会携带别的字符。

wordpress 会缓存部分 JS 资源,记得在 vue-spa.php 文件中修改 vuespa_load_vues() 函数的版本号

打包

打包的过程,就是优化整合各代码的过程,我们定位到 vites 文件夹下,输入以下代码进行打包。

  1. npm run build

完成后,如下所示

我们可以在如下位置找到打包后的文件

  1. /vites/dist/

导入

有了打包好的 JS 文件和 CSS 文件,现在,我们将其在菜单中导入,修改 vue-spa.php 文件中的函数vuespa_load_vues()为以下内容

  1. //载入所需 JS 和 CSS 资源 并传递数据
  2. function vuespa_load_vues($hook)
  3. {
  4. //判断当前页面是否是指定页面,是则继续加载
  5. if('toplevel_page_vuespa_id'!= $hook){
  6. return;
  7. }
  8. //版本号
  9. $ver ='55';
  10. //加载到页面顶部
  11. wp_enqueue_style('vite', plugin_dir_url(__FILE__).'vites/dist/index.css', array(), $ver,false);
  12. //加载到页面底部
  13. wp_enqueue_script('vite', plugin_dir_url(__FILE__).'vites/dist/index.js', array(), $ver,true);
  14. $pf_api_translation_array = array(
  15. 'route'=> esc_url_raw(rest_url()),//路由
  16. 'nonce'=> wp_create_nonce('wp_rest'),//验证标记
  17. 'data'=> vuespa_data(),//自定义数据
  18. );
  19. wp_localize_script('vite','dataLocal', $pf_api_translation_array);//传给vite项目
  20. }
  21. //样式加载到后台
  22. add_action('admin_enqueue_scripts','vuespa_load_vues');

这里,我们无需手动载入 vue.js 和 Axios.js 文件了,打包后的 index.js 文件中,都准备好了,减少了不必要的资源开销。

添加type属性

注意,因为我们打包后的 index.js 文件,是一个模块,需要给其添加一个 type 属性才能正常生效。

我们在 vue-spa.php 文件底部,添加以下代码,给导入的 index.js 添加type属性,

  1. //模块导入
  2. function add_type_attribute_to_script($tag, $handle)
  3. {
  4. // 在这里判断需要添加 type 属性的 JS 文件,比如文件名包含 xxx.js
  5. if(strpos($tag,'index.js')!==false){
  6. // 在 script 标签中添加 type 属性
  7. $tag = str_replace('<script','<script type="module"', $tag);
  8. }
  9. return $tag;
  10. }
  11. add_filter('script_loader_tag','add_type_attribute_to_script',10,2);

效果如下:

  1. //使用函数前
  2. <script src='<http://localhost:10004/wp-content/plugins/vue-spa/vites/dist/index.js?ver=53>' id='vite-js'></script>
  3. //使用函数后
  4. <script type="module" src='<http://localhost:10004/wp-content/plugins/vue-spa/vites/dist/index.js?ver=53>' id='vite-js'></script>

补充

若您需要在本地进行开发和预览,您可能需要以下几个知识

修改 index.html

/vites/

修改文件为以下内容,

  1. <body>
  2. <divid="vuespa"></div>
  3. <scripttype="module"src="/src/main.js"></script>
  4. </body>

拿不到传来的 dataLocal

dataLocal 的值是外部传来的,vite 并不知道,您可以通过临时添加以下内容,进行模仿,但记得,在打包前进行注释。

  1. const dataLocal ={
  2. route:"http://localhost:5173/wp-json/",
  3. nonce:"asdf",
  4. data:{
  5. user:[
  6. { id:1, name:"111"},
  7. { id:2, name:"222"},
  8. ],
  9. },
  10. };

vue-spa.php完整代码

  1. <?php
  2. /*
  3. Plugin Name: Vue - SPA
  4. Plugin URI: https://www.npc.ink
  5. Description: 将vue构建的页面嵌入WordPress 中并产生交互
  6. Author: Muze
  7. Author URI: https://www.npc.ink
  8. Version: 1.0.0
  9. */
  10. //接口
  11. require_once plugin_dir_path(__FILE__).'interface.php';
  12. //创建一个菜单
  13. function vuespa_create_menu_page()
  14. {
  15. add_menu_page(
  16. 'VueSpa选项',// 此菜单对应页面上显示的标题
  17. 'VueSpa',// 要为此实际菜单项显示的文本
  18. 'administrator',// 哪种类型的用户可以看到此菜单
  19. 'vuespa_id',// 此菜单项的唯一ID(即段塞)
  20. 'vuespa_menu_page_display',// 呈现此页面的菜单时要调用的函数的名称 'vuespa_menu_page_display'
  21. 'dashicons-admin-customizer',//图标 - 默认图标
  22. '500.1',//位置
  23. );
  24. }// end vuespa_create_menu_page
  25. add_action('admin_menu','vuespa_create_menu_page');
  26. //菜单回调 - 展示的内容
  27. function vuespa_menu_page_display()
  28. {
  29. ?>
  30. <!--在默认WordPress“包装”容器中创建标题-->
  31. <divclass="wrap">
  32. <!--标题-->
  33. <h2><?php echo esc_html(get_admin_page_title());?></h2>
  34. <!--提供Vue挂载点-->
  35. <divid="vuespa">此内容将在挂载Vue后被替换{{data}}</div>
  36. </div>
  37. <?php
  38. //展示准备的数据
  39. echo "<pre>";
  40. print_r(vuespa_data());
  41. echo "</pre>";
  42. echo "<h3>调用选项值</h3>";
  43. echo get_option('dataOne');
  44. echo "<br/>";
  45. echo get_option('dataTwo');
  46. echo "<br/>";
  47. print_r(get_option('dataName'));
  48. echo "<br/>";
  49. echo get_option('dataImage');
  50. echo "<br/>";
  51. echo get_option('dataSelectedImage');
  52. }// vuespa_menu_page_display
  53. //载入所需 JS 和 CSS 资源 并传递数据
  54. function vuespa_load_vues($hook)
  55. {
  56. //判断当前页面是否是指定页面,是则继续加载
  57. if('toplevel_page_vuespa_id'!= $hook){
  58. return;
  59. }
  60. //版本号
  61. $ver ='53';
  62. //加载到页面顶部
  63. wp_enqueue_style('vite', plugin_dir_url(__FILE__).'vites/dist/index.css', array(), $ver,false);
  64. //加载到页面底部
  65. wp_enqueue_script('vite', plugin_dir_url(__FILE__).'vites/dist/index.js', array(), $ver,true);
  66. $pf_api_translation_array = array(
  67. 'route'=> esc_url_raw(rest_url()),//路由
  68. 'nonce'=> wp_create_nonce('wp_rest'),//验证标记
  69. 'data'=> vuespa_data(),//自定义数据
  70. );
  71. wp_localize_script('vite','dataLocal', $pf_api_translation_array);//传给vite项目
  72. }
  73. //样式加载到后台
  74. add_action('admin_enqueue_scripts','vuespa_load_vues');
  75. //准备待传输的数据
  76. function vuespa_data()
  77. {
  78. $person =[
  79. "str"=>"Hello, world! - Npcink",
  80. "num"=>25,
  81. "city"=>[1,2,3,4,5],
  82. "user"=> vuespa_get_user_meat(),
  83. ];
  84. return $person;
  85. }
  86. //整理并提供用户信息
  87. function vuespa_get_user_meat()
  88. {
  89. //获取所有角色
  90. $editable_roles = wp_roles()->roles;
  91. $roles = array_keys($editable_roles);
  92. //获取除了'subscriber'(订阅者)角色之外的所有角色的用户数据
  93. $subscriber_key = array_search('subscriber', $roles,true);
  94. if(false!== $subscriber_key){
  95. $roles = array_slice($roles,0, $subscriber_key);
  96. }
  97. $users = get_users(array('role__in'=> $roles));
  98. //转为关联数组
  99. $user_data = array_map(function($user){
  100. return[
  101. 'id'=> $user->ID,
  102. 'name'=> $user->display_name,
  103. ];
  104. }, $users);
  105. return $user_data;
  106. }
  107. //模块导入
  108. function add_type_attribute_to_script($tag, $handle)
  109. {
  110. // 在这里判断需要添加 type 属性的 JS 文件,比如文件名包含 xxx.js
  111. if(strpos($tag,'index.js')!==false){
  112. // 在 script 标签中添加 type 属性
  113. $tag = str_replace('<script','<script type="module"', $tag);
  114. }
  115. return $tag;
  116. }
  117. add_filter('script_loader_tag','add_type_attribute_to_script',10,2);

总结

本章节中,我们使用 Vite 对 index.js 文件进行了打包等处理,基于现在的 Node 生态,您还可以

  • 使用 mockjs 提供拦截,更方便的进行本地开发
  • 使用现成的前端框架提升开发效率,例如 Element Plus
  • 使用 Pinia 进行数据的统一管理
  • 使用第三方库,实现数据校验
  • 使用TS约束变量类型,提升代码健壮性

因篇幅原因,此处不再赘述。

下面是我使用 Element Plus 做出的下拉选项卡,比浏览器默认的好用多了

如果您能坚持看到这里,相信您也会有所收获,希望您能基于此教程,做出更多有趣和实用的代码

第四节:Vue3 开发WordPress设置选项 - 添加图片上传功能

阅读(9)

承接上文,在常用选项中,图片上传和选择功能用的较多,本节来实现这一功能,我们着重讨论功能实现,更多美化样式以及优化性能问题,可自行探索。此处为了便于大家理解,代码言简意赅。

预览

需求如下

  • 提供一个图片上传按钮
  • 可展示选中的图片
  • 提供清空图片按钮

流程如下

效果如下

修改 index.js

存储图片链接值

我们添加键 dataImage 用于存储选中图片的链接

  1. //存储选项值
  2. const datas =Vue.reactive({
  3. dataImage:"",
  4. });

在获取选项数据时进行赋值,这里,我修改了原来的函数名为 get_option ,更加简洁易懂。

  1. //获取数据
  2. const get_option =()=>{
  3. axios
  4. .post(dataLocal.route +"pf/v1/get_option", datas,{
  5. headers:{
  6. "X-WP-Nonce": dataLocal.nonce,
  7. "Content-Type":"application/json",
  8. },
  9. })
  10. .then((response)=>{
  11. //省略
  12. datas.dataImage = data.dataImage;
  13. })
  14. .catch((error)=>{
  15. window.alert("连接服务器失败或后台读取出错!数据读取失败");
  16. console.log(error);
  17. });
  18. };

添加图片上传功能

我们通过以下两个函数,实现图片上传功能

  1. //上传图片
  2. const upload_img =(file)=>{
  3. const formData =newFormData();
  4. formData.append("file", file);
  5. return axios
  6. .post(dataLocal.route +"wp/v2/media", formData,{
  7. headers:{
  8. "X-WP-Nonce": dataLocal.nonce,
  9. "Content-Type":"multipart/form-data",
  10. },
  11. })
  12. .then((response)=>{
  13. // 图片上传成功后的处理逻辑
  14. const data = response.data;
  15. //返回图片URL
  16. return data.source_url;
  17. })
  18. .catch((error)=>{
  19. console.error(error);
  20. // 图片上传失败后的处理逻辑
  21. });
  22. };
  23. //处理图片上传事件
  24. const update_img =(event)=>{
  25. const file =event.target.files[0];
  26. upload_img(file).then((url)=>{
  27. //将拿到的图片URL传给图片变量
  28. datas.dataImage = url;
  29. });
  30. };

添加清空功能

添加以下代码,实现清空功能

  1. //清空选择图片
  2. const clear_img =()=>{
  3. datas.dataImage ="";
  4. };

添加展示模版

为了模版可以拿到对应的功能,记得将需要的功能函数返回出来。

  1. return{
  2. datas,
  3. siteData,
  4. update_option,
  5. update_img,
  6. clear_img,
  7. };

模版代码如下

  1. <inputtype="file" @change.native="update_img"><br/>
  2. <buttontype="button" @click="clear_img">清理</button><br/>
  3. <imgstyle="width:300px;height:auto;" :src=datas.dataImagev-if=datas.dataImage><hr/>

此时刷新页面,尝试选择图片并保存,即可看到我们完成了图片上传功能,清理按钮也能正常工作,记得修改选项后点击保存按钮。

选择媒体库文件

修改 index.js 文件

流程

  • 创建函数,通过REST API 从WordPress 媒体库中获取图片数据
  • vue将获取的图片数据展示在前端,并提供选择按钮
  • 选中图片后,将值传给选项值
  • 保存

效果

创建变量

我们创建变量用于存储获取的图片信息,为了便于扩展,这里使用reactive

  1. //存储获取的值
  2. const getData =Vue.reactive({
  3. //存储获取的媒体库值
  4. mediaList:[],
  5. });

添加新选项

  1. //存储选项值
  2. const datas =Vue.reactive({
  3. //省略
  4. dataSelectedImage:"",
  5. });

获取数据也得加上

  1. //获取数据
  2. const get_option =()=>{
  3. axios
  4. .post(dataLocal.route +"pf/v1/get_option", datas,{
  5. headers:{
  6. "X-WP-Nonce": dataLocal.nonce,
  7. "Content-Type":"application/json",
  8. },
  9. })
  10. .then((response)=>{
  11. //省略
  12. datas.dataSelectedImage = data.dataSelectedImage;
  13. })
  14. .catch((error)=>{
  15. window.alert("连接服务器失败或后台读取出错!数据读取失败");
  16. console.log(error);
  17. });
  18. };

获取媒体库图片

通过以下函数获取图片信息并存储信息进键 mediaList 中

  1. //获取媒体库图片
  2. const getMediaList =()=>{
  3. axios
  4. .get(dataLocal.route +"wp/v2/media")
  5. .then((response)=>{
  6. getData.mediaList = response.data;
  7. })
  8. .catch((error)=>{
  9. console.error(error);
  10. });
  11. };

选择媒体库图片

添加以下代码进行选择

  1. //从媒体库选中图片
  2. const selectImage =(imageUrl)=>{
  3. datas.dataSelectedImage = imageUrl;
  4. };

添加模版

将模版用的数据进行导出

  1. return{
  2. datas,
  3. siteData,
  4. update_option,
  5. update_img,
  6. clear_img,
  7. selectImage,
  8. getMediaList,
  9. getData,
  10. };

添加模版内容

  1. <button @click="getMediaList">获取媒体库图片</button>
  2. <divstyle="max-width:800px;;display: flex; margin:1em0;">
  3. <divv-for="media in getData.mediaList" :key="media.id"style="float: left;">
  4. <img :src="media.source_url"style="max-width:150px; height:auto;vertical-align: top;">
  5. <button @click="selectImage(media.source_url)">选择</button>
  6. </div>
  7. </div>
  8. <h2>{{datas.dataSelectedImage ? "已" : "未"}}选择图片</h2>
  9. <img :src="datas.dataSelectedImage"v-if="datas.dataSelectedImage"style="width:150px;height:auto;"><hr/>

获取选项值

在php 中,可通过以下方法获取选项值

  1. echo "<br/>";
  2. echo get_option('dataImage');
  3. echo "<br/>";
  4. echo get_option('dataSelectedImage');

改进

上述代码还有很多改进空间,此处为便于演示以及篇幅原因,仅叙于此。

以下是几个可以优化的点

  • 选中图片后无需上传至WordPress即可预览,点击保存按钮后再上传图片,
  • 若图片选项有值,则使用上传后的图片链接进行图片预览
  • 优化清理按钮,或做成组件,可复用

本地图片预览功能

  1. const datas =Vue.reactive({
  2. dataImage:"",
  3. });
  4. const update_img =(event)=>{
  5. const file =event.target.files[0];
  6. const formData =newFormData();
  7. //预览图片
  8. datas.dataImage = URL.createObjectURL(file);
  9. }
  10. <input type="file"@change.native="update_img"><br/>
  11. <img style="width: 300px;height: auto;":src=datas.dataImage ><hr/>

总结

本节我们学习了从本地上传图片和从媒体库选择图片。

代码部分比较乱,尤其是模版部分,这些都会在后续的打包中进行解决的。

能坚持到这里,你已经很棒了,相信到这里,你已经掌握了复选框,布尔值,单选框,多选框等内容,我就不再过多赘述了。

下一节,我们将使用Vite对现有 JS 文件进行打包,并使用一些基础的CSS样式对现有选项进行外观美化,并进一步研究数据校验问题。

第三节:Vue3 开发WordPress设置选项 - 从筛选功能研究前后台数据交互

阅读(15)

承接上文,我们制作了一个简单的,带有两个输入框和一个保存按钮的设置选项,现在,他已经能实现了最基础的填写信息并保存到 WordPress 后台,供PHP调用选项值的功能。

现在,我们更进一步,为其添加人员筛选功能,我们制作一个下列框,从中通过用户名进行选择,并将选择好的用户 ID 通过数组提供给后端使用。大概流程如下

最终效果如下

准备用户数据

一般的网站用户量较大,大部分都是“订阅者”,为了减少传输数据压力,我们在获取用户数据时将其排除掉
为了将拿到数据方便给 JS 使用,降低 JS 使用数据难度,我们将其整理成如下结构

在 vue-spa.php 文件底部添加以下代码

  1. //整理并提供用户信息
  2. function vuespa_get_user_meat()
  3. {
  4. //获取所有角色
  5. $editable_roles = wp_roles()->roles;
  6. $roles = array_keys($editable_roles);
  7. //获取除了'subscriber'(订阅者)角色之外的所有角色的用户数据
  8. $subscriber_key = array_search('subscriber', $roles,true);
  9. if(false!== $subscriber_key){
  10. $roles = array_slice($roles,0, $subscriber_key);
  11. }
  12. $users = get_users(array('role__in'=> $roles));
  13. //转为关联数组
  14. $user_data = array_map(function($user){
  15. return[
  16. 'id'=> $user->ID,
  17. 'name'=> $user->display_name,
  18. ];
  19. }, $users);
  20. return $user_data;
  21. }

您可以参考此方法,做出分类筛选、文章筛选、标签筛选等筛选功能,只需按结构提供数据即可。

传递用户数据

我们通过 PHP 将数据传给 JS ,以供使用,我们修改 vue-spa.php 文件中的 vuespa_data() 函数,改为以下内容

  1. function vuespa_data()
  2. {
  3. $person =[
  4. "str"=>"Hello, world! - Npcink",
  5. "num"=>25,
  6. "city"=>[1,2,3,4,5],
  7. "user"=> vuespa_get_user_meat(),
  8. ];
  9. return $person;
  10. }

刷新菜单页面,我们就能看到如上图的效果。

JS 准备页面

JS 中拿到传来的数据,需要将其渲染至页面上,修改index.js为以下内容

  1. //vite/dist/index.js
  2. console.log(dataLocal.data.user);
  3. constApp={
  4. setup(){
  5. //存储传来的值
  6. const siteData = dataLocal.data;
  7. //存储选项值
  8. const datas =Vue.reactive({
  9. dataOne:"",
  10. dataTwo:"",
  11. dataName:[],
  12. });
  13. //获取数据
  14. const vuespa_get_option =()=>{
  15. axios
  16. .post(dataLocal.route +"pf/v1/get_option", datas,{
  17. headers:{
  18. "X-WP-Nonce": dataLocal.nonce,
  19. "Content-Type":"application/json",
  20. },
  21. })
  22. .then((response)=>{
  23. const data = response.data;
  24. datas.dataOne = data.dataOne;
  25. datas.dataTwo = data.dataTwo;
  26. datas.dataName = data.dataName;
  27. })
  28. .catch((error)=>{
  29. window.alert("连接服务器失败或后台读取出错!数据读取失败");
  30. console.log(error);
  31. });
  32. };
  33. //省略部分代码
  34. return{ datas, siteData, vuespa_update_option };
  35. },
  36. template:`
  37. 文本框1:<input type="text" v-model="datas.dataOne"><br/>
  38. 文本框2:<input type="text" v-model="datas.dataTwo"><hr/>
  39. 用户选择:<select v-model="datas.dataName" multiple>
  40. <option v-for="option in siteData.user" :key="option.id" :value="option.id">
  41. {{ option.name }}
  42. </option>
  43. </select>
  44. <p>你选择了:{{ datas.dataName }}</p><br/>
  45. 按住command(control)按键即可进行多选<hr/>
  46. <button class="button button-primary" @click="vuespa_update_option">保存</button>`,
  47. };
  48. Vue.createApp(App).mount("#vuespa");

为了方便大家看出不同,我省略了部分未修改的代码,其详细内容,可见上一节。
主要内容如下

  • 新建变量 siteData 存储传来的数据
  • 新建数组变量 dataName 存储选中数组
  • 在获取数据中,通过 datas.dataName = data.dataName;拿到默认值

修改保存接口

原有的保存接口无法识别数组,若您此时修改选中的值并点击保存按钮,刷新页面后会丢失选中的值。

修改 interface.php 文章的保存选项功能函数 update_option_by_RestAPI() 为以下内容

  1. //保存Option
  2. function update_option_by_RestAPI($data)
  3. {
  4. //判断是否是管理员
  5. if(current_user_can('manage_options')){
  6. //转为JSON对象 - 重点,这里没有true,是转为对象
  7. $dataArray = json_decode($data->get_body());
  8. //存储结果
  9. $result =new stdClass();
  10. //循环保存选项
  11. foreach($dataArray as $option_name => $value){
  12. //判断,是否为对象
  13. if(is_object($value)){
  14. //是非空数组,循环保存值
  15. foreach($value as $arr => $data){
  16. //更新值
  17. update_option($arr, $data);
  18. }
  19. }else{
  20. //不是对象,则表示只有一个选项需要保存。
  21. update_option($option_name, $value);
  22. }
  23. $result->$option_name = $value;
  24. }
  25. //返回成功信息
  26. returnnew WP_REST_Response(array(
  27. 'success'=>true,
  28. 'message'=>"已保存!"
  29. ),200);
  30. }else{
  31. //返回失败信息
  32. returnnew WP_Error('save_error','保存失败!', array('status'=>500));
  33. }
  34. }

此函数的功能可见注释,现在,刷新页面,在下列列表中进行筛选,然后点击保存按钮,刷新页面,可看到值是正常保存的,

使用

此时,您可以使用 get_option 在PHP中拿到选项中的值,正如上一节中提到的。

  1. echo get_option('dataName');

注意,这会展示数组ID

总结

这一节我们添加了下拉列表多选功能,并没有解决上一节提到的问题,不要急,我发现目前展示功能类型很方便,我准备在展示完所有常见功能类型后,再去解决上一节提到的问题。

第二节:Vue3 开发WordPress设置选项 - 从输入框开始,配置基础设置选项

阅读(30)

承接上文,我们解决了PHP传递数据给JS的问题,现在,我们可以创建两个输入框和一个保存按钮。

  • 输入框存储和修改选项值
  • 保存按钮用于保存选项值

在页面加载的开始获取选项的值,将其作为输入框的默认值,修改输入框内容后点击保存按钮,将输入框的值通过 REST API 保存到 WordPress 中,并通过 get_option() 函数拿到对应的值。

流程介绍

详细流程如下

后端准备

在后端,我们要准备两个接口,分别是

  • 读取选项接口,在页面的开始获取选项初始值
  • 保存选项接口,点击保存按钮时保存选项

这两个接口会在后续的前端开发中用到。为了方便大家理解, 我们会从最简单的函数开始,然后逐步加大难度。

我们在 vue-spa 文件夹下新建 interface.php 文件,用来放置接口相关的代码。
填入以下代码,创建两个接口

  1. //接口文件
  2. function vuespa_create_api()
  3. {
  4. register_rest_route('pf/v1','/get_option/', array(// 完整命名空间为:/wp-json/pf/v1/
  5. 'methods'=>'POST',
  6. 'callback'=>'get_option_by_RestAPI',
  7. ));
  8. register_rest_route('pf/v1','/update_option/', array(// 完整命名空间为:/wp-json/pf/v1/
  9. 'methods'=>'POST',
  10. 'callback'=>'update_option_by_RestAPI',
  11. 'permission_callback'=>function(){
  12. return current_user_can('manage_options');// 只有管理员才有权限修改
  13. },
  14. ));
  15. }
  16. add_action('rest_api_init','vuespa_create_api');

读取选项接口

我们填入以下内容

  1. //读取Option
  2. //仅支持一对一的数据请求
  3. function get_option_by_RestAPI($data)
  4. {
  5. //将传递数据转成数组类型
  6. $dataArray = json_decode($data->get_body(),true);
  7. //新建数组
  8. $return = array();
  9. //循环获取对应选项ID的值,并将其存储在对应关联数组中,若拿不到值,则为空
  10. foreach($dataArray as $option_name => $value){
  11. $return[$option_name]= get_option($option_name)? get_option($option_name):"";
  12. }
  13. return $return;
  14. }

保存选项接口

我们填入以下内容

  1. //保存Option
  2. //一对一保存
  3. function update_option_by_RestAPI($data)
  4. {
  5. //判断是否是管理员
  6. if(current_user_can('manage_options')){
  7. //将传递数据转成数组类型
  8. $dataArray = json_decode($data->get_body(),true);
  9. //循环保存选项
  10. foreach($dataArray as $option_name => $value){
  11. update_option($option_name, $value);
  12. }
  13. //返回成功信息
  14. returnnew WP_REST_Response(array(
  15. 'success'=>true,
  16. 'message'=>"已保存!"
  17. ),200);
  18. }else{
  19. //返回失败信息
  20. returnnew WP_Error('save_error','保存失败!', array('status'=>500));
  21. }
  22. }

引入

现在,我们的 interface.php 文件准备好了,我们在 vue-spa.php 中添加以下代码引入该文件,

  1. //接口
  2. require_once plugin_dir_path(__FILE__).'interface.php';

前端准备

我们需要用到 Vue3 和 Axios ,当然,只要能实现功能的,你喜欢的任何工具均可。

我们还需要在菜单页面中展示两个输入框和一个保存按钮

载入资源

修改 vue-spa.php 中的 vuespa_load_vues() 函数,我们需要引入 vue3 和 Axios 资源。改为以下代码

  1. //载入所需 JS 和 CSS 资源 并传递数据
  2. function vuespa_load_vues($hook)
  3. {
  4. //判断当前页面是否是指定页面,是则继续加载
  5. if('toplevel_page_vuespa_id'!= $hook){
  6. return;
  7. }
  8. //版本号
  9. $ver ='53';
  10. //加载到页面顶部
  11. wp_enqueue_style('vite', plugin_dir_url(__FILE__).'vite/dist/index.css', array(), $ver,false);
  12. //加载到页面底部
  13. wp_enqueue_script('vue','https://unpkg.com/vue@3/dist/vue.global.js', array(), $ver,true);
  14. wp_enqueue_script('axios','https://unpkg.com/axios/dist/axios.min.js', array(), $ver,true);
  15. wp_enqueue_script('vite', plugin_dir_url(__FILE__).'vite/dist/index.js', array(), $ver,true);
  16. $pf_api_translation_array = array(
  17. 'route'=> esc_url_raw(rest_url()),//路由
  18. 'nonce'=> wp_create_nonce('wp_rest'),//验证标记
  19. 'data'=> vuespa_data(),//自定义数据
  20. );
  21. wp_localize_script('vite','dataLocal', $pf_api_translation_array);//传给vite项目
  22. }
  23. //样式加载到后台
  24. add_action('admin_enqueue_scripts','vuespa_load_vues');

在这里,为了让我们的 vue3 的 JS 文件能正确拿到文档节点,我们将 JS 的加载都从 false 改为 true ,这样,JS 就会加载到页面底部了。

现在,我们就能在菜单页面正常使用 vue3 的功能了。

准备输入框和按钮

我们修改 index.js 文件为以下代码

  1. //vite/dist/index.js
  2. constApp={
  3. setup(){
  4. //初始值
  5. const datas =Vue.reactive({
  6. dataOne:"",
  7. dataTwo:"",
  8. });
  9. //获取数据
  10. const vuespa_get_option =()=>{
  11. axios
  12. .post(dataLocal.route +"pf/v1/get_option", datas,{
  13. headers:{
  14. "X-WP-Nonce": dataLocal.nonce,
  15. "Content-Type":"application/json",
  16. },
  17. })
  18. .then((response)=>{
  19. const data = response.data;
  20. datas.dataOne = data.dataOne;
  21. datas.dataTwo = data.dataTwo;
  22. })
  23. .catch((error)=>{
  24. window.alert("连接服务器失败或后台读取出错!数据读取失败");
  25. console.log(error);
  26. });
  27. };
  28. //保存数据
  29. const vuespa_update_option =()=>{
  30. axios
  31. .post(dataLocal.route +"pf/v1/update_option", datas,{
  32. headers:{
  33. "X-WP-Nonce": dataLocal.nonce,
  34. },
  35. })
  36. .then((response)=>{
  37. alert("保存成功");
  38. })
  39. .catch((error)=>{
  40. alert("保存失败");
  41. console.log(error);
  42. });
  43. };
  44. //页面初始加载
  45. Vue.onMounted(()=>{
  46. console.log("简简单单");
  47. vuespa_get_option();
  48. });
  49. return{ datas, vuespa_update_option };
  50. },
  51. template:
  52. '文本框1:<input type="text" v-model="datas.dataOne"><br/>文本框2:<input type="text" v-model="datas.dataTwo"><hr/><button class="button button-primary" @click="vuespa_update_option">保存</button>',
  53. };
  54. Vue.createApp(App).mount("#vuespa");

作用如下:

  • 创建了一个响应式变量datas
  • 创建了获取数据函数vuespa_get_option()通过上一节传来的网址进行拼接后发出post请求,并在标头中传入验证信息。
  • 创建了保存数据函数vuespa_update_option(),与上述函数作用类似
  • 我们在页面的开始,就加载vuespa_get_option()函数,将拿到的值作为输入框的默认值
  • 我们通过 vue3 的模版,提供了两个 input输入框和一个按钮,
  • input 的值与 datas 的变量进行双向绑定,按钮的点击事件绑定vuespa_get_option()函数进行数据保存

现在,刷新页面,就能看到我们创建的输入框,进行简单的修改,点击保存按钮,就能将数据保存到 WordPress 了。

wordpress 会缓存部分 JS 文件,建议您每次修改 JS 文件后,都修改函数 vuespa_load_vues() 中的版本号信息

原理浅析

当我们进入菜单页 VusSpa 时,观察控制台,会有一个get_option的请求,内容如下

发出了请求,拿到了选项值,并将其作为默认值。

现在,我们修改输入框中的值,点击保存按钮,会触发update_option,内容如下:

已将我修改的值传出了,并返回保存成功的信息。

调用

跟传统方法一样,我们使用get_option来调用对应的选项值。

选项已准备好,我们开始调用,为了方便展示调用的数据,我们将选项值在菜单页中展示,
我们修改回调函数vuespa_menu_page_display(),添加以下内容

  1. echo "<h3>调用选项值</h3>";
  2. echo get_option('dataOne');
  3. echo "<br/>";
  4. echo get_option('dataTwo');

可看到以下效果

问题

目前的功能是可以用了,但还有些问题

  • 只能一对一的获取数据,在一些复杂场景中,这种数据结构很不友好
  • 目前是手动载入 Vue3 和 Axios文件,加载缓慢,导致页面都加载好了,选项内容有些许延迟,有闪动感,体验不好
  • 为了简单的功能,载入了 vue3 这个庞然大物,而且vue3的很多功能都没用上。
  • 没有人员筛选、文章筛选和分类筛选之类的,与 WordPress 产生交互的功能,

针对以上问题,我们将在下一节中继续进行优化改进。