本页目录

承接上文,我们将常用选项中的两个功能给实现了,这一节,我们将对 JS 资源进行打包,最终产物只有一个 JS 文件和一个 CSS 文件,可以极大的提升网页加载速度,减少依赖。
打包后的文件中,就包含了 vue3 和 Axios 的代码内容,不必通过 URL 引入 vue3 的所有功能了。而且,在打包之前,还能对各种选项进行自定义的验证,还能使用各种前端框架,对选项进行美化。
更重要的是,使用打包后的文件,可以明显减少页面卡顿。
- 本系列代码分享在 GitHub 中,希望能帮助大家理解
- https://GitHub.com/muze-page/vue-spa
本节流程

准备打包环境
我们使用 Vite 进行打包,您需要提前安装 node 环境,
创建环境
使用 VS Code 打开我们的 vue-spa 文件夹,通过 Ctrl+~ 打开终端,输入以下命令创建 Vue 3 项目
npm create vite@latest vites -- --template vue稍等片刻后,分别执行以下几个命令
cd vites npm install npm run dev会提示一个网址,

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

我们同时按下 Ctrl+c 暂停当前运行,回到控制台中。
安装 Axios
在控制台中输入以下命令,安装 Axios
npm install axios安装完成后,打开 vites 文件夹下的 package.json 文件即可看到如下提示

即代表您安装成功了。
重写 index.js
现在,我们需要重写 index.js 文件,我们在 vites/src/components/ 文件夹下,新建文件 Option.vue 文件。写入以下内容
<script setup>import { reactive, onMounted } from "vue";import axios from "axios";const siteData = dataLocal.data;
//存储获取的值const getData = reactive({ //存储获取的媒体库值 mediaList: [],});
//存储选项值const datas = reactive({ dataOne: "", dataTwo: "", dataName: [], dataImage: "", dataSelectedImage: "",});
//获取数据const get_option = () => { axios .post(dataLocal.route + "pf/v1/get_option", datas, { headers: { "X-WP-Nonce": dataLocal.nonce, "Content-Type": "application/json", }, }) .then((response) => { const data = response.data; datas.dataOne = data.dataOne; datas.dataTwo = data.dataTwo; datas.dataName = data.dataName; datas.dataImage = data.dataImage; datas.dataSelectedImage = data.dataSelectedImage; }) .catch((error) => { window.alert("连接服务器失败或后台读取出错!数据读取失败"); console.log(error); });};
//保存数据const update_option = () => { console.log(datas); axios .post(dataLocal.route + "pf/v1/update_option", datas, { headers: { "X-WP-Nonce": dataLocal.nonce, }, }) .then((response) => { alert("保存成功"); }) .catch((error) => { alert("保存失败"); console.log(error); });};
//上传图片const upload_img = (file) => { const formData = new FormData(); formData.append("file", file); return axios .post(dataLocal.route + "wp/v2/media", formData, { headers: { "X-WP-Nonce": dataLocal.nonce, "Content-Type": "multipart/form-data", }, }) .then((response) => { // 图片上传成功后的处理逻辑 const data = response.data; //返回图片URL return data.source_url; }) .catch((error) => { console.error(error); // 图片上传失败后的处理逻辑 });};
//处理图片上传事件const update_img = (event) => { const file = event.target.files[0]; upload_img(file).then((url) => { //将拿到的图片URL传给图片变量 datas.dataImage = url; });};
//清空选择图片const clear_img = () => { datas.dataImage = "";};
//获取媒体库图片const getMediaList = () => { axios .get(dataLocal.route + "wp/v2/media") .then((response) => { getData.mediaList = response.data; }) .catch((error) => { console.error(error); });};
//从媒体库选中图片const selectImage = (imageUrl) => { datas.dataSelectedImage = imageUrl;};
//页面初始加载onMounted(() => { //获取选项值 get_option();});</script>
<template> <!--两个输入框--> 文本框1:<input type="text" v-model="datas.dataOne" /><br /> 文本框2:<input type="text" v-model="datas.dataTwo" /> <hr /> <!--用户选择--> 用户选择:<select v-model="datas.dataName" multiple> <option v-for="option in siteData.user" :key="option.id" :value="option.id"> {{ option.name }} </option> </select> <p>你选择了:{{ datas.dataName }}</p> <br /> 按住command(control)按键即可进行多选 <hr /> <!--图片上传--> <input type="file" @change.native="update_img" /><br /> <button type="button" @click="clear_img">清理</button><br /> <img :src="datas.dataImage" v-if="datas.dataImage" /> <hr /> <!--获取媒体库图片--> <button @click="getMediaList">获取媒体库图片</button> <div class="box"> <div v-for="media in getData.mediaList" :key="media.id" style="float: left"> <img :src="media.source_url" /> <button @click="selectImage(media.source_url)">选择</button> </div> </div> <h2>{{ datas.dataSelectedImage ? "已" : "未" }}选择图片</h2> <img :src="datas.dataSelectedImage" v-if="datas.dataSelectedImage" /> <hr />
<button class="button button-primary" @click="update_option">保存</button></template>
<style scoped>img { max-width: 150px; height: auto; vertical-align: top;}.box { max-width: 800px; display: flex; margin: 1em 0;}</style>这里,对原有写法在语法糖 setup 的帮助下,进行了部分重写,再抽离了部分 CSS 样式,使得整体的代码更加健壮和容易维护了。
当然,还有更多方法可以优化,为了便于讲解,这里不再赘述。
修改 App.js
/vites/src/
模块制作好了,我们在 App.vue 文件中导入,写入以下内容
<script setup>//import HelloWorld from "./components/HelloWorld.vue";import Option from "./components/Option.vue";</script>
<template> <Option></Option></template>
<style scoped></style>将我们写的组件展示出来
修改 main.js
/vites/src/
在之前的章节中,我们提前准备的 ID 是 vuespa ,所以,需要修改下此文件为以下内容
import { createApp } from 'vue'//import './style.css'import App from './App.vue'
createApp(App).mount('#vuespa')在这里,我还把默认的 CSS 样式给注释了
修改 vite.config.js
/vites/src/
为了让打包后的文件名与我们原有的文件名保持一致,我们需要修改下打包细节,替换该文件为以下内容
import { defineConfig } from "vite";import vue from "@vitejs/plugin-vue";
// https://vitejs.dev/config/export default defineConfig({ plugins: [vue()], build: { rollupOptions: { output: { // 指定 chunk 文件名(含导出的代码) //chunkFileNames: 'js/[name].js', // 指定静态资源文件名(不含导出的代码) //assetFileNames: 'assets/[name].[ext]', entryFileNames: "index.js", assetFileNames: "[name][extname]", chunkFileNames: "[name].js", }, }, },});这样,打包后就会产出 index.js 和 index.css 文件了,而不会携带别的字符。
WordPress 会缓存部分 JS 资源,记得在 vue-spa.php 文件中修改 vuespa_load_vues() 函数的版本号
打包
打包的过程,就是优化整合各代码的过程,我们定位到 vites 文件夹下,输入以下代码进行打包。
npm run build完成后,如下所示

我们可以在如下位置找到打包后的文件
/vites/dist/导入
有了打包好的 JS 文件和 CSS 文件,现在,我们将其在菜单中导入,修改 vue-spa.php 文件中的函数vuespa_load_vues()为以下内容
//载入所需 JS 和 CSS 资源 并传递数据function vuespa_load_vues($hook){ //判断当前页面是否是指定页面,是则继续加载 if ('toplevel_page_vuespa_id' != $hook) { return; } //版本号 $ver = '55'; //加载到页面顶部 wp_enqueue_style('vite', plugin_dir_url(__FILE__) . 'vites/dist/index.css', array(), $ver, false); //加载到页面底部 wp_enqueue_script('vite', plugin_dir_url(__FILE__) . 'vites/dist/index.js', array(), $ver, true);
$pf_api_translation_array = array( 'route' => esc_url_raw(rest_url()), //路由 'nonce' => wp_create_nonce('wp_rest'), //验证标记 'data' => vuespa_data(), //自定义数据 ); wp_localize_script('vite', 'dataLocal', $pf_api_translation_array); //传给vite项目}//样式加载到后台add_action('admin_enqueue_scripts', 'vuespa_load_vues');这里,我们无需手动载入 vue.js 和 Axios.js 文件了,打包后的 index.js 文件中,都准备好了,减少了不必要的资源开销。
添加 type 属性
注意,因为我们打包后的 index.js 文件,是一个模块,需要给其添加一个 type 属性才能正常生效。
我们在 vue-spa.php 文件底部,添加以下代码,给导入的 index.js 添加 type 属性,
//模块导入function add_type_attribute_to_script($tag, $handle){ // 在这里判断需要添加 type 属性的 JS 文件,比如文件名包含 xxx.js if (strpos($tag, 'index.js') !== false) { // 在 script 标签中添加 type 属性 $tag = str_replace('<script', '<script type="module"', $tag); } return $tag;}add_filter('script_loader_tag', 'add_type_attribute_to_script', 10, 2);效果如下:
//使用函数前<script src='<http://localhost:10004/wp-content/plugins/vue-spa/vites/dist/index.js?ver=53>' id='vite-js'></script>
//使用函数后<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/
修改文件为以下内容,
<body> <div id="vuespa"></div> <script type="module" src="/src/main.js"></script> </body>拿不到传来的 dataLocal
dataLocal 的值是外部传来的,vite 并不知道,您可以通过临时添加以下内容,进行模仿,但记得,在打包前进行注释。
const dataLocal = { route: "http://localhost:5173/wp-json/", nonce: "asdf", data: { user: [ { id: 1, name: "111" }, { id: 2, name: "222" }, ], },};vue-spa.php 完整代码
<?php/*Plugin Name: Vue - SPAPlugin URI: https://www.npc.inkDescription: 将vue构建的页面嵌入WordPress 中并产生交互Author: MuzeAuthor URI: https://www.npc.inkVersion: 1.0.0*/
//接口require_once plugin_dir_path(__FILE__) . 'interface.php';
//创建一个菜单function vuespa_create_menu_page(){ add_menu_page( 'VueSpa选项', // 此菜单对应页面上显示的标题 'VueSpa', // 要为此实际菜单项显示的文本 'administrator', // 哪种类型的用户可以看到此菜单 'vuespa_id', // 此菜单项的唯一ID(即段塞) 'vuespa_menu_page_display', // 呈现此页面的菜单时要调用的函数的名称 'vuespa_menu_page_display' 'dashicons-admin-customizer', //图标 - 默认图标 '500.1', //位置 );} // end vuespa_create_menu_pageadd_action('admin_menu', 'vuespa_create_menu_page');
//菜单回调 - 展示的内容function vuespa_menu_page_display(){?>
<!--在默认WordPress“包装”容器中创建标题--> <div class="wrap"> <!--标题--> <h2><?php echo esc_html(get_admin_page_title()); ?></h2> <!--提供Vue挂载点--> <div id="vuespa">此内容将在挂载Vue后被替换{{data}}</div> </div>
<?php
//展示准备的数据 echo "<pre>"; print_r(vuespa_data()); echo "</pre>";
echo "<h3>调用选项值</h3>"; echo get_option('dataOne'); echo "<br/>"; echo get_option('dataTwo'); echo "<br/>"; print_r(get_option('dataName')); echo "<br/>"; echo get_option('dataImage'); echo "<br/>"; echo get_option('dataSelectedImage');} // vuespa_menu_page_display
//载入所需 JS 和 CSS 资源 并传递数据function vuespa_load_vues($hook){ //判断当前页面是否是指定页面,是则继续加载 if ('toplevel_page_vuespa_id' != $hook) { return; } //版本号 $ver = '53'; //加载到页面顶部 wp_enqueue_style('vite', plugin_dir_url(__FILE__) . 'vites/dist/index.css', array(), $ver, false); //加载到页面底部 wp_enqueue_script('vite', plugin_dir_url(__FILE__) . 'vites/dist/index.js', array(), $ver, true);
$pf_api_translation_array = array( 'route' => esc_url_raw(rest_url()), //路由 'nonce' => wp_create_nonce('wp_rest'), //验证标记 'data' => vuespa_data(), //自定义数据 ); wp_localize_script('vite', 'dataLocal', $pf_api_translation_array); //传给vite项目}//样式加载到后台add_action('admin_enqueue_scripts', 'vuespa_load_vues');
//准备待传输的数据function vuespa_data(){ $person = [ "str" => "Hello, world! - Npcink", "num" => 25, "city" => [1, 2, 3, 4, 5], "user" => vuespa_get_user_meat(), ]; return $person;}
//整理并提供用户信息function vuespa_get_user_meat(){ //获取所有角色 $editable_roles = wp_roles()->roles; $roles = array_keys($editable_roles); //获取除了'subscriber'(订阅者)角色之外的所有角色的用户数据 $subscriber_key = array_search('subscriber', $roles, true); if (false !== $subscriber_key) { $roles = array_slice($roles, 0, $subscriber_key); }
$users = get_users(array('role__in' => $roles));
//转为关联数组 $user_data = array_map(function ($user) { return [ 'id' => $user->ID, 'name' => $user->display_name, ]; }, $users);
return $user_data;}
//模块导入function add_type_attribute_to_script($tag, $handle){ // 在这里判断需要添加 type 属性的 JS 文件,比如文件名包含 xxx.js if (strpos($tag, 'index.js') !== false) { // 在 script 标签中添加 type 属性 $tag = str_replace('<script', '<script type="module"', $tag); } return $tag;}add_filter('script_loader_tag', 'add_type_attribute_to_script', 10, 2);总结
本章节中,我们使用 Vite 对 index.js 文件进行了打包等处理,基于现在的 Node 生态,您还可以
- 使用 mockjs 提供拦截,更方便的进行本地开发
- 使用现成的前端框架提升开发效率,例如 Element Plus
- 使用 Pinia 进行数据的统一管理
- 使用第三方库,实现数据校验
- 使用 TS 约束变量类型,提升代码健壮性
因篇幅原因,此处不再赘述。
下面是我使用 Element Plus 做出的下拉选项卡,比浏览器默认的好用多了

如果您能坚持看到这里,相信您也会有所收获,希望您能基于此教程,做出更多有趣和实用的代码来。
最新文章
- 后续文章持续撰写中,点个关注,获取平台最新文章推送。
- 技术有限,还望诸位协助勘误,于评论区指出,
- 常一文多发,最新勘定和增补文章于下方链接给出
- https://www.npc.ink/277313.html