iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 24
0

Nuxt 的 plugins 有分為 「VueComponent 實例」、「Nuxt context」以及「整合至兩者」的三種不同類型的 plugin,今天除了介紹 Nuxt plugin 的寫法之外,另外看看我們可以怎麼改寫 axios 讓 API 更好使用!

Vue Plugin

基本就是 Vue 原生寫法:

  1. plugins 底下新增 plugin (vueLog.js),並且引入 Vue。
import Vue from 'vue';
  1. 撰寫 plugin,其中 install 第一個參數 Vue 是自動注入的。
const vueLog = {
    install (Vue, options) {
        const image = (options && options.image)
            ? options.image
            : 'https://member.ithome.com.tw/avatars/132702?s=ithelp';
        const consoleCSS = `line-height: 30px; background:url("${image}"); background-size:cover;`;
        Vue.prototype.vueLog = logData => {
            console.group('%c     ', consoleCSS, 'Vue APP: ');
            console.log(logData);
            console.groupEnd();
        };
    }
}
  1. 透過 Vue.use 掛載 plugin。
import Vue from 'vue';
import vueLog from '../helpers/vueLog';

Vue.use(vueLog, {});
  1. nuxt.config.jsplugins 加入 plugin。
// ...
plugins: [
    '@/plugins/vueLog'
],
// ...
  1. 在 Vue component 中,可以透過 this.vueLog 使用 plugin。
    https://ithelp.ithome.com.tw/upload/images/20190925/20112580jTrvXxFPX1.png

Nuxt Plugin

若 plugin 單純只要加在 Nuxt context 供 Nuxt 使用,則步驟如下:

  1. plugins 底下新增 plugin (nuxtLog.js)

  2. 撰寫 plugin,其中第一個參數 context 是自動注入的。

export default (context) => {
    context.nuxtLog = logData => {
        console.group('Nuxt APP: ');
        console.log(logData);
        console.groupEnd();
    };
};
  1. nuxt.config.jsplugins 加入 plugin。
// ...
plugins: [
    '@/plugins/nuxtLog'
],
// ...
  1. 在 Nuxt 相關的 lifecycle 中,可以透過 context.nuxtLog 使用 plugin。
    https://ithelp.ithome.com.tw/upload/images/20190925/20112580868lLTjSZ4.png
    (你問我為甚麼沒有兩津? 因為 Nuxt 不給顯示啊 (哭奔 XD))

Both Plugin

如果 plugin 同時在 VueComponent 與 Nuxt 中使用,可以依下列步驟建立。

  1. plugins 底下新增 plugin (log.js)

  2. 撰寫 plugin,兩個參數 contextinject 都是自動注入的。

export default (context, inject) => {
    inject('log', (logData) => {
        const image = 'https://member.ithome.com.tw/avatars/132702?s=ithelp';
        const consoleCSS = `line-height: 30px; background:url("${image}"); background-size:cover;`;
        console.group('%c     ', consoleCSS, 'APP: ');
        console.log(logData);
        console.groupEnd();
    });
}
  1. nuxt.config.jsplugins 加入 plugin。
// ...
plugins: [
    '@/plugins/log'
],
// ...
  1. 使用上都跟前面 Vue plugin 以及 Nuxt plugin 相同,有兩點要注意:

    • 所有的 plugin 名稱都會加上 「$」如: this.$log
    • Nuxt plugin 會掛在 context.app 底下

    使用結果:
    https://ithelp.ithome.com.tw/upload/images/20190925/20112580G86Nl5DEVm.png

axios wrapper

在了解怎麼寫 plugin 之後,我們來寫個 axios 的 wraper 吧!

  1. 在 cmd 中執行下列指令安裝套件 (最近下載量超級低,大家順手捐發票救救老殘窮 XD)
npm i lara-validator --save
  1. 在專案中增加 configs 資料夾 (可以用 GIT Submodule),並擺放之前為了生成 FormRequest 的 JSON 檔案

  2. 建立 plugin (api.js)

import { LaraValidator } from 'lara-validator';

// 驗證 API request body
const validate = (data, rules) => {
  if (rules && data) {
    const validator = new LaraValidator(rules, data);
    const isValid = validator.valid();
    if (!isValid) {
      const error = new Error('Front end validate error');
      error.number = 422;
      error.validateError = validator.errorMessage;
      throw error;
    }
  }
};

// 遍歷要轉為 FormData 的 object
const parsingObject = (formData, data, parentKey) => {
  const isCurlyBracketsObject = data && typeof data === 'object' && !(data instanceof Date) && !(data instanceof File);
  if (isCurlyBracketsObject) {
    Object.keys(data).forEach(key => {
      const nextParentKey = (parentKey) ? `${parentKey}[${key}]` : key;
      parsingObject(formData, data[key], nextParentKey);
    });
  } else {
    const value = data || '';
    formData.append(parentKey, value);
  }
};

// 將 object 轉為 FormData
const objectToFormData = (data) => {
  const formData = new FormData();
  parsingObject(formData, data);
  return formData;
};

// 呼叫 API
const invokeAPI = async (method, uri, data, token) => {
    const headers = {
        Authorization: `Bearer ${token}`,
    };
    switch (method) {
        case 'get':
        return (await axios.get(uri, { headers })).data;
        case 'post':
        return (await axios.post(uri, data, { headers })).data;
        case 'form':
        data = objectToFormData(data);
        return (await axios.post(uri, data, { headers })).data;
    }
};

// plugin
export default ({ store }, inject) => {
    inject('api', async (method, url, data, validator) => {
        const token = store.getters.userToken;
        try {
            validate(data, validator);
            return await invokeAPI(method, url, data, token);
        } catch (error) {
            // do something
        }
    });
}

在上面 api plugin 的例子當中,呼叫 API 之前會先透過 validator 參數驗證 request data,接著我們將常用的 axios 進一步的打包,包括在 request headers 當中加入 token,另外我們也將 getpost 以及「以 FormData 作為 request data 的 post」整合在一起。這樣在使用上可以免去不斷引入各項資料之外,另外當 API 需要傳送檔案的時候也比較方便。

下面是 post 編輯頁使用 api plugin 的範例:

import { validators: { AccessPostByIdRequest, EditPostRequest } } from '../../configs/post';

export default {
    name: 'index',
    data() {
        return {
            post: undefined,
        };
    },
    methods: {
        async updatePost() {
            try {
                await this.$api('post', '/v1/post/edit', EditPostRequest.body, this.post);
            } catch (error) {
                // ...
            }
        }
    },
    async asyncData({ app, params }) {
        let post = undefined;
        try {
            const getPostByIdData = { postId: params['id'] };
            const { data } = await app.$api('post', '/v1/post', AccessPostByIdRequest.body, getPostByIdData);
            post = data;
        } catch (error) {
            // ...
        }

        return {
            post,
        }
    }
}

從今天的範例來看 Nuxt plugin 其實滿好寫的,我們也看到可以根據專案需求另外擴充 axios 成為 plugin,尤其 API 的 request 和 response 通常都有一定的資料結構跟相關訊息,透過整合成 plugin 可以將許多重複處理的流程集中整合,讓 code 變得更簡潔。

在 Nuxt 中因為是 SSR ,所以畫面渲染的過程如果有遇到 windowdocument 等前端資料就會出問題,因此明天會介紹 Nuxt 系列的最後一篇 client-only!

P.s. 如果大大在使用 lara-validator 的過程中有 bug 或是建議增加甚麼功能歡迎 拍打餵食 ,我是說留言跟我說 XD


上一篇
Day 23. Vuex 和 Cookie 哪個好? 小朋友才做決定,我兩個都要
下一篇
Day 25. 說好的 window 和 document 呢?
系列文
RRR撞到不負責之 Laravel + Nuxt.js 踩坑全紀錄31

尚未有邦友留言

立即登入留言