Vue & REST API打造WordPress主题选项页

8月5日 · 2017年

前言

很久没写博客了,今年转行做前端啦。Vue作为前端最火的三大框架之一,不玩儿到WordPress上怎么行呢?但Vue本身就是一个模板引擎,而WordPress作为文章发布系统,前台是需要被浏览器抓取的,要是前台使用模板引擎填充势必对搜索引擎不友好。所以就干脆在后台玩玩儿吧。于是尝试做了一下WordPress的主题设置选项页。传统的主题设置选项页做法已经很成熟并且有框架了,我这里就不再提了,下面简单介绍如何使用Vue这种前端技术来实现。

学习本文需有 Vue.js 的使用经验

技术栈

Vue 2.x – 前端数据绑定与模板引擎(至少需要IE9浏览器)

Element UI – 基于Vue的UI框架

WP REST API – 用于前端与后端的数据传输,WordPress4.7以上自带,老版本WP需插件

新增 REST API

由于我们需要做option的保存与读取,而WordPress并没有这个接口,因此我们需要手动写一个REST API接口用来保存和读取Option。因为vue对数据的保存是{name1:value1,name2:value2}的形式存储的,因此我们将api也写成这样的形式,方便数据提交。

此处需学习WordPress官方文档的REST API相关细节

在functions.php文件中新增下面的代码来手动增加API:

//使用REST API需要的一点点授权工作
wp_register_script( 'pf_restapi', '' );
$pf_api_translation_array = array(
    'route' => esc_url_raw( rest_url() ),
    'nonce' => wp_create_nonce( 'wp_rest' ),
    );
wp_localize_script( 'pf_restapi', 'pandastudio_framework', $pf_api_translation_array );
wp_enqueue_script( 'pf_restapi' );

//注册API地址
add_action( 'rest_api_init', function () {
    register_rest_route( 'vue', '/get_option/', array(
        'methods' => 'post',
        'callback' => 'get_option_by_RestAPI',
        ));
    register_rest_route( 'vue', '/update_option/', array(
        'methods' => 'post',
        'callback' => 'update_option_by_RestAPI',
        ));
});

//读取Option
function get_option_by_RestAPI( $data ) {
    $dataArray = json_decode($data->get_body(),true);
    $return = array();
    foreach ($dataArray as $option_name => $value) {
        $return[$option_name] = get_option($option_name) ? get_option($option_name) : "";
    }
    return $return;
}

//保存Option
function update_option_by_RestAPI( $data ) {
    if (current_user_can('manage_options')) {
        $dataArray = json_decode($data->get_body(),true);
        foreach ($dataArray as $option_name => $value) {
            update_option($option_name,$value);
        }
    }
}

新增页面

在functions.php中写增加后台设置页的代码

add_action('admin_menu', 'add_option_rest_page');
function add_option_rest_page(){
  add_menu_page( '主题设置', '主题设置', 'manage_options', 'pf_options', 'create_Page', '', 60 );
}

此处需学习 add_menu_page 的使用方法,详见官方文档

接下来写创建页面的函数,由于我们要使用Vue和ElementUI来制作配置页面,因此需要先引入Vue和ElementUI。此处我们引入在线脚本作为示例

function create_Page () {
?>
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-default/index.css">
<div id="vue_rest" class="wrap">
     <!-- Vue模板:在这里编写Element-UI的代码 -->
</div>
<script type="text/javascript">
    //Vue脚本:在这里编写Vue的对象
</script>
<?php
}

引入在线脚本会影响加载速度,强烈建议用于生产环境时使用主题本地的脚本和样式

写到这里本文就可以结束了(吃瓜群众:你逗我?!),下面只是使用Element UI和调用API的方法,任何一个前端开发者都应该能熟练上手

创建示例文本框

根据Element UI的开发文档,在上面“Vue模板”处做一个Element UI表单并增加两个输入框

<el-form ref="form" :model="form" label-width="100px">
    <el-form-item label="第一个输入框">
        <el-input v-model="sampleInput1"></el-input>
    </el-form-item>
    <el-form-item label="第二个输入框">
        <el-input v-model="sampleInput2"></el-input>
    </el-form-item>
</el-form>

然后在“Vue脚本”中新增一个Vue对象,绑定表单的输入框数据

var vue_rest = new Vue({
        el: "#vue_rest",
        data() {
            return {
                sampleInput1 :"",
                sampleInput2 :"",
            }
        },
    })

现在,打开后台设置页面,可以看到如下所示的界面了。我们所输入的字段都通过Vue绑定到了脚本的data里面

但是数据要怎么提交到后台呢?还需要一个保存按钮来使用一开始我们写好的API

制作保存按钮

根据Element UI的文档在刚才的表单后面做一个按钮,并绑定一个Vue method

<el-button type="primary" @click="save_config">保存</el-button>

在vue对象中新增这个method,然后用ajax去提交数据

methods: {
            save_config() {
                _this = this;
                jQuery.ajax({
                    url: pandastudio_framework.route + 'vue/update_option',
                    type: 'POST',
                    beforeSend: function ( xhr ) {
                        xhr.setRequestHeader( 'X-WP-Nonce', pandastudio_framework.nonce );
                    },
                    data: JSON.stringify(_this._data),
                })
                .done(function(data) {
                    _this.$message.success('保存成功!')
                })
                .fail(function() {
                    _this.$notify.error({
                        title: '保存失败',
                        message: '连接服务器失败或后台保存出错!'
                    });
                })
            }
        }

效果如下

读取数据

我们发现,在进入页面的时候,数据仍然是空的,因此需要在vue被挂载的时候去读取一次数据,然后保存到data中。根据vue的生命周期文档,增加挂载时执行的脚本

mounted: function() {
            var _this = this;
            jQuery.ajax({
                url: pandastudio_framework.route + 'vue/get_option',
                type: 'POST',
                beforeSend: function ( xhr ) {
                    xhr.setRequestHeader( 'X-WP-Nonce', pandastudio_framework.nonce );
                },
                data: JSON.stringify(_this._data),
            })
            .done(function(data) {
                for (var key in data) {
                    _this[key] = data[key];
                }
            })
            .fail(function() {
                _this.loading = false;
                _this.show = false;
                _this.$alert('连接服务器失败或后台读取出错!', '数据读取失败', {
                    confirmButtonText: '确定',
                })
            })
        }

至此,一个简单的后台页面读取和保存数据的功能就做好了。需要增加数据字段的时候,在Element UI的表单部分增加输入框,将值绑定到data就可以了。

在WordPress主题模板中读取存储字段的方法依旧是

<?php get_option("sampleInput1"); ?>

示例代码

在自带主题Twenty Seventeen中测试有效(粘贴到functions.php最末尾)

wp_register_script( 'pf_restapi', '' );
$pf_api_translation_array = array(
  'route' => esc_url_raw( rest_url() ),
  'nonce' => wp_create_nonce( 'wp_rest' ),
);
wp_localize_script( 'pf_restapi', 'pandastudio_framework', $pf_api_translation_array );
wp_enqueue_script( 'pf_restapi' );
//新增API地址
add_action( 'rest_api_init', function () {
  register_rest_route( 'vue', '/get_option/', array(
    'methods' => 'post',
    'callback' => 'get_option_by_RestAPI',
  ));
  register_rest_route( 'vue', '/update_option/', array(
    'methods' => 'post',
    'callback' => 'update_option_by_RestAPI',
  ));
});
//API方法
function get_option_by_RestAPI( $data ) {
  $dataArray = json_decode($data->get_body(),true);
  $return = array();
  foreach ($dataArray as $option_name => $value) {
    $return[$option_name] = get_option($option_name) ? get_option($option_name) : "";
  }
  return $return;
}
function update_option_by_RestAPI( $data ) {
  if (current_user_can('manage_options')) {
    $dataArray = json_decode($data->get_body(),true);
     foreach ($dataArray as $option_name => $value) {
       update_option($option_name,$value);
     }
  }
}
add_action('admin_menu', 'add_option_rest_page');
function add_option_rest_page(){
  add_menu_page( '主题设置', '主题设置', 'manage_options', 'pf_options', 'create_Page', '', 60 );
}
function create_Page () {
?>
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-default/index.css">
<div id="vue_rest" class="wrap">
<!-- 在这里编写element-ui的代码 -->
<el-form ref="form" :model="form" label-width="100px">
    <el-form-item label="第一个输入框">
        <el-input v-model="sampleInput1"></el-input>
    </el-form-item>
    <el-form-item label="第二个输入框">
        <el-input v-model="sampleInput2"></el-input>
    </el-form-item>
</el-form>
<el-button type="primary" @click="save_config">保存</el-button>
</div>
<script type="text/javascript">
    //在这里编写vue的对象
    var vue_rest = new Vue({
        el: "#vue_rest",
        data() {
            return {
                sampleInput1 :"",
                sampleInput2 :"",
            }
        },
        mounted: function() {
            var _this = this;
            jQuery.ajax({
                url: pandastudio_framework.route + 'vue/get_option',
                type: 'POST',
                beforeSend: function ( xhr ) {
                    xhr.setRequestHeader( 'X-WP-Nonce', pandastudio_framework.nonce );
                },
                data: JSON.stringify(_this._data),
            })
            .done(function(data) {
                for (var key in data) {
                    _this[key] = data[key];
                }
            })
            .fail(function() {
                _this.loading = false;
                _this.show = false;
                _this.$alert('连接服务器失败或后台读取出错!', '数据读取失败', {
                    confirmButtonText: '确定',
                })
            })
        },
        methods: {
            save_config() {
                _this = this;
                jQuery.ajax({
                    url: pandastudio_framework.route + 'vue/update_option',
                    type: 'POST',
                    beforeSend: function ( xhr ) {
                        xhr.setRequestHeader( 'X-WP-Nonce', pandastudio_framework.nonce );
                    },
                    data: JSON.stringify(_this._data),
                })
                .done(function(data) {
                    _this.$message.success('保存成功!')
                })
                .fail(function() {
                    _this.$notify.error({
                        title: '保存失败',
                        message: '连接服务器失败或后台保存出错!'
                    });
                })
            }
        }
    })
</script>
<?php
}

拓展

下面提供一些可以发散的思路,可以把这个设置页做得更好。代码较为复杂,不再示例,但都是基于本文实现的:

  1. 更多组件:本文只是简单描述了使用Vue、Element UI、Rest API来制作主题设置页的简单方法。利用前端技术完全可以扩展成更加复杂的表单,如:下拉菜单、单选框、色彩选择器、开关、slider滑动条等,甚至还可以在顶部加上Element UI做好的“Tab菜单”
  2. 导入导出:通过浏览器的生成Blob对象接口以及文件读取接口,还可以在无需增加后台PHP代码的前提下,完全用前端来实现option数据的导入导出
  3. 配色:Element UI的配色是浅蓝色,与WordPress不搭,可以用Element文档的 “在线主题生成工具” 生成适合WP的色彩主题

这是我最终做出来的效果,开开脑洞,可以做出更多功能

本文著作权归本人所有,未经许可谢绝转载!

3 条回应
验证码
输入运算符及数字使等式成立
{{comment.validate_num1}} = {{comment.validate_num2}}
点赞 确定
退出登录?
取消 确定
  1. Gguoan Dong 2018-9-14 · 19:52

    大神好,我是日常过来拜访

  2. 爆爆小妹 2017-10-12 · 20:27

    哎!你这是让前后端都下岗的节奏啊!!

    • Panda 2017-10-12 · 22:14

      这样做可以让WordPress开发变得更方便