第五節: Vue3 開發WordPress設置選項 - 使用Vite打包JS資源

    本節使用 Vite 對之前的 JS 資源進行了打包處理,提升了運行效率,降低了資源開銷,直觀的降低了頁面卡頓感。基于現代化的 Node生態,還可以做出各種功能,例如數據校驗,展示圖標等無限可能。

    承接上文,我們將常用選項中的兩個功能給實現了,這一節,我們將對 JS 資源進行打包,最終產物只有一個 JS 文件和一個 CSS 文件,可以極大的提升網頁加載速度,減少依賴。

    打包后的文件中,就包含了 vue3 和 Axios 的代碼內容,不必通過URL 引入 vue3 的所有功能了。而且,在打包之前,還能對各種選項進行自定義的驗證,還能使用各種前端框架,對選項進行美化。

    更重要的是,使用打包后的文件,可以明顯減少頁面卡頓。

    • 本系列代碼分享在 GitHub 中,希望能幫助大家理解
    • https://github.com/muze-page/vue-spa

    本節流程

    第五節: Vue3 開發WordPress設置選項 - 使用Vite打包JS資源

    準備打包環境

    我們使用 Vite 進行打包,您需要提前安裝 node 環境,

    創建環境

    使用 VS Code 打開我們的vue-spa文件夾,通過Ctrl+~ 打開終端,輸入以下命令創建 Vue 3 項目

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

    稍等片刻后,分別執行以下幾個命令

      cd vites
      npm install
      npm run dev

    會提示一個網址,

    第五節: Vue3 開發WordPress設置選項 - 使用Vite打包JS資源


    我們將其在瀏覽器中打開,即可看到如下顯示

    第五節: Vue3 開發WordPress設置選項 - 使用Vite打包JS資源

    我們同時按下 Ctrl+c 暫停當前運行,回到控制臺中。

    安裝Axios

    在控制臺中輸入以下命令,安裝 Axios

    npm install axios

    安裝完成后,打開vites文件夾下的 package.json 文件即可看到如下提示

    第五節: Vue3 開發WordPress設置選項 - 使用Vite打包JS資源


    即代表您安裝成功了。

    重寫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

    完成后,如下所示

    第五節: Vue3 開發WordPress設置選項 - 使用Vite打包JS資源


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

    /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 - SPA 
    Plugin URI: http://www.kartiktrivedi.com
    Description: 將vue構建的頁面嵌入WordPress 中并產生交互
    Author: Muze
    Author URI: http://www.kartiktrivedi.com
    Version: 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_page 
    add_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 做出的下拉選項卡,比瀏覽器默認的好用多了

    第五節: Vue3 開發WordPress設置選項 - 使用Vite打包JS資源

    如果您能堅持看到這里,相信您也會有所收獲,希望您能基于此教程,做出更多有趣和實用的代碼來。

    最新文章

    • 后續文章持續撰寫中,點個關注,獲取平臺最新文章推送。
    • 技術有限,還望諸位協助勘誤,于評論區指出,
    • 常一文多發,最新勘定和增補文章于下方鏈接給出
    • http://www.kartiktrivedi.com/277313.html
    教程

    第六節:Vue3 開發WordPress設置選項 - 添加對象傳值和數據校驗

    2023-6-29 22:11:00

    教程

    01:講透WordPress 菜單開發 - 在菜單中添加Tab選項卡

    2023-6-29 22:46:00

    ??
    Npcink上的部份代碼及教程來源于互聯網,僅供網友學習交流,若您喜歡本文可附上原文鏈接隨意轉載。
    無意侵害您的權益,請發送郵件至 1355471563#qq.com 或點擊右側 私信:Muze 反饋,我們將盡快處理。
    0 條回復 A文章作者 M管理員
      暫無討論,說說你的看法吧
    ?
    個人中心
    購物車
    優惠劵
    今日簽到
    有新私信 私信列表
    搜索
    主站蜘蛛池模板: 日本不卡免费新一区二区三区| 在线免费视频一区二区| 国产福利91精品一区二区三区| 国产另类TS人妖一区二区| 久热国产精品视频一区二区三区| 国产成人精品一区二区三区无码| 国产精品亚洲一区二区在线观看| 99精品久久精品一区二区| 人妻少妇精品视频三区二区一区| 国偷自产一区二区免费视频| 国产精品一区二区香蕉| 国产一区二区福利| 国产福利电影一区二区三区| 无码国产精品一区二区免费式直播 | 无码人妻一区二区三区免费看| 国产伦一区二区三区免费| 亚洲码一区二区三区| 99久久精品费精品国产一区二区| 国产一区二区三区四| 欧美亚洲精品一区二区| 亚洲国产精品第一区二区| 成人区人妻精品一区二区不卡网站 | 免费在线视频一区| 国产精品99精品一区二区三区| 农村乱人伦一区二区| 日韩精品无码一区二区三区AV | 日本成人一区二区三区| 福利一区二区三区视频午夜观看| 亚洲欧美日韩一区二区三区在线 | 亚洲bt加勒比一区二区| 国产一区二区三区高清视频 | 国产精品成人国产乱一区| 国产伦精品一区二区三区在线观看 | 无码人妻一区二区三区av| 亚洲日韩一区二区三区| 中文字幕无码不卡一区二区三区 | 国产一在线精品一区在线观看| 精品中文字幕一区二区三区四区| 精品久久久久久中文字幕一区| 国产Av一区二区精品久久| 色一情一乱一区二区三区啪啪高|