axios create() + Vue-loading-overlay 實戰應用

之前寫了 axios 的基本用法與搭配 axios create() 的使用方法,之前有看過前輩把 loading 寫在 interceptors 攔截器中,在 .interceptors.request.use()中用 Jquery() 控制 CSS 打開 loading 效果,.interceptors.response.use() 中關掉 loading 效果。在這裡我想利用 pinia 共用 loading 的狀態,利用 .interceptors.request.use() 與 .interceptors.response.use(),開關 vue-loading-overlay 的效果。

先看這兩篇:

也要稍微了解一下 pinia 的用法。

安裝 pinia

npm install pinia

安裝 axios

npm install axios

安裝 vue-loading-overlay

npm install vue-loading-overlay@^6.0 

檔案結構

resource/index.js 是有關 axios create() 跟 interceptors 相關設定。

stores/user.js 是有關 pinia 共用 loading 控制變數的設定。

store/user.js 設定

isLoading 是一個布林值,初始值為 false,用來控制 loading 元件的開關,放在pinia stores中,要傳到 resource/index.js 及有用到 loading 元件的 vue 檔中共用。

import { defineStore } from 'pinia';

export const useUserStore = defineStore('user', {
  state: () => {
    return { isLoading: false };
  },
  // 也可以这样定义
  // state: () => ({ isLoading :false })
});

resource/index.js 設定

在resource/index.js 中引入 axios 與 stores 的 user.js 。

import axios from 'axios';

import { useUserStore } from '@/stores/user.js';

設定 axios.create

export const randomUser = axios.create();

randomUser.defaults.baseURL = '<https://randomuser.me/>';

.interceptors.request.use 設定

要把控制 loading 的共享變數放入,這樣就可以在打 API 的 request 與收到 response 時跑 loading 效果:

const userStore = useUserStore();
userStore.isLoading = true;

注意:const userStore = useUserStore(); 如果放在 randomUser.interceptors.request.use() 的外面會報錯。

完整的 resource/index.js

import axios from 'axios';

import { useUserStore } from '@/stores/user.js';
// const userStore = useUserStore() 放在外面這裡 會報錯!!!
// const userStore = useUserStore()
export const randomUser = axios.create();

randomUser.defaults.baseURL = '<https://randomuser.me/>';

randomUser.interceptors.request.use(
  (config) => {
    const userStore = useUserStore();
    // 打開 loading
    userStore.isLoading = true;
    console.log('useUserStore', userStore.isLoading);
    return config;
  },
  (error) => {
    const userStore = useUserStore();
    // 關掉 loading
    useUserStore.isLoading = false;
    return Promise.reject(error);
  }
);

randomUser.interceptors.response.use(
  (response) => {
    const userStore = useUserStore();
    // 關掉 loading
    userStore.isLoading = false;
    return response;
  },
  function (error) {
    const userStore = useUserStore();
    // 關掉 loading
    userStore.isLoading = false;
    return Promise.reject(error);
  }
);

在 vue 檔中設定

引入 store 的變數

// 引入 store
import { useUserStore } from '@/stores/user.js';

// store
const userStore = useUserStore();

引入 loading 元件

import Loading from 'vue-loading-overlay';

在 <template> 中放入 loading 元件(在這裡以元件方式來做,而非 plugin 方式)。其中 color 與 is-full-page 要綁定到 color 跟 fullPage 這兩個 ref 變數。

v-model:active=”userStore.isLoading” 則是綁定到 store 的 isLoading 變數,控制 loading效果的開關。

<template>
  <header>
    <loading
      v-model:active="userStore.isLoading"
      :can-cancel="true"
      :color="color"
      :is-full-page="fullPage"
    />
    <div class="wrapper">Loading 效果</div>
  </header>
</template>

設定完以上,可以發現 randomUser.get(‘api/’) 時,在 request 與 response 之間會有 loading 動畫效果。

<script setup>
import { ref } from 'vue';
// 引入 axios 攔截器
import { randomUser } from '@/resource/index.js';
// 引入 store
import { useUserStore } from '@/stores/user.js';
// 引入 vue-loading-overlay
import Loading from 'vue-loading-overlay';

// store
const userStore = useUserStore();

// vue-loading-overlay 設定
const color = ref('#ff0000');
const fullPage = ref(true);

randomUser.get('api/').then((res) => {
  console.log('res', res);
});
</script>

<template>
  <header>
    <loading
      v-model:active="userStore.isLoading"
      :can-cancel="true"
      :color="color"
      :is-full-page="fullPage"
    />
    <div class="wrapper">Loading 效果</div>
  </header>
</template>

如果打 API 時如果要帶 tokent 也可以放在 .interceptors.request.use 中,就不用每次打 API 都要寫一遍,這也是實戰時常用的技巧。

randomUser.interceptors.request.use(request => {
    request.headers.common.Authorization = `Bearer ${localStorage.getItem('token')}`
    return request
})

Leave a Reply

Your email address will not be published. Required fields are marked *