Mark Ku's Blog

Vue 全域 / 非全域 Loading 的等待特效 ( 支援多個併發請求 )

解決問題

在前端工程中,常常會透過 ajax 向後端做非同步資料交換,但api 可能會因各種因素延遲回應,為提昇更好的使用者體驗,通常都會做一個過場的動畫,讓使用者得知正在加載資料。

而大多數套件設計的相當簡易,背後可能只是一個 boolean 值,在多個併發的非同步的ajax request ,只要有任一個 request,提前回來就被關掉 loading 的過場動畫。

設計原理

在 vuex 中 做個計數器功能, ajax 發出 request 時,就呼叫 store 中的increment方法,讓count 就加1,完成或失敗則 count 減1,並提供 isLoading 的 getter 給畫面判斷, 透過封裝 axios 來由外部傳入參數決定需不需要 show loading 轉場動畫。

  1. request 進來 就 count + 1
  2. request 失敗 count - 1 ( 參數錯誤,不會往下走,也不會走到 reponse )
  3. response 成功 count - 1
  4. response 失敗 count - 1 ( time out 、 server error、 cancel )

Vuex 的程式碼

import { generatorUUID } from '@/utils'
var _ = require('lodash')

const state = {

  count: []
}
// getters
const getters = {
  isLoading: (state) => {
    return state.count.length > 0
  },
  removeByItems: (state) => {
    return state.count.filter(x => x.isRemoveByRequestId === true)
  }
}
// mutations
const mutations = {
  increment(state, requestId) {
    const isRemoveByRequestId = requestId ! '' && requestId ! undefined

    requestId = requestId || generatorUUID()

    var newLoading = { requestId: requestId, isRemoveByRequestId: isRemoveByRequestId }

    state.count.push(newLoading)
  },
  decrement(state, requestId) {
    if (requestId) {
      var index = state.count.findIndex(item => item.requestId === requestId)
      state.cart.splice(index, 1)
    } else {
      const newCount = _.cloneDeep(state.count).filter(x => x.isRemoveByRequestId === false)
      if (newCount.length > 0) {
        newCount.shift()
        state.count = newCount
      }
    }
  }
}
// actions
const actions = {

}
export default {
  namespaced: true,
  state,
  getters,
  actions,
  mutations
}

封裝 axios

function apiAxios(method, url, params, hideLoading, requestId) {
  // 顯示loading
  const appParams = {}
  params = { ...appParams, ...params }
  const httpDefault = {
    method: method,
    baseURL: baseURL,
    url: url,
    hideLoading: hideLoading,
    requestId: requestId,
    // `params` 是即將與請求一起發送的 URL 參數
    // `data` 是作為請求主體被發送的數據
    params: method = 'GET' || method = 'DELETE' ? params : null,
    data: method = 'POST' || method = 'PUT' ? params : null,
    timeout: 100000
  }

  if (httpDefault['hideLoading'] === false) {
    store.commit('loading/increment', httpDefault['requestId'])
  }

  return new Promise((resolve, reject) => {
    axios(httpDefault)
      .then((res) => {
        if (httpDefault['hideLoading'] === false) {
          store.commit('loading/decrement', requestId)
        }
        resolve(successState(res))
      })
      .catch((response) => {
        if (httpDefault['hideLoading'] === false) {
          store.commit('loading/decrement', requestId)
        }
       
        errorState(response)
      })
  })
}

在 Layout 加上 Loading box 的組件,並 監聽 getter 中的 isLoading 屬性,來顯示 Loading box 。

Vue 程式碼:LoadingBox 元件與 isLoading 監聽
Vue 程式碼:LoadingBox 元件與 isLoading 監聽

倘若 Call Api 時不需要顯示 loading 時,則多傳遞一個 true 的參數

Vue.prototype.getAxios(getAxios(`{process.env.VUE_APP_BASE_API}/api/v1/home/query`, null, true)

為了之後擴充,預留一個彈性,各別元件若需要控制,可以由外面把 request id 傳入,元件在去 watch loading count 物件中 isRemoveByRequestId 屬性的資料

export function getHomeQuery(requestId = '') { return Vue.prototype.getAxios(getAxios(`{process.env.VUE_APP_BASE_API}/api/v1/home/query`, null, true, requestId) }

延伸議題 - Skeleton

現今越來越多網站 採用 Skeleton 提昇使用者的使用體驗,可以之後列成優化項目。

參考資料連結

作者

Mark Ku

擁有 10+ 年經驗的資深軟體工程師,現為 AI 應用 Builder,專注於大型平台架構與簡化複雜系統設計,從電商系統到訂閱與收費平台,結合 AI Agent、AI 整合與自動化開發,打造高效率且可持續演進的產品技術基礎。閱讀更多

覺得這篇有幫助?

作者做的免費工具、每日 Podcast 與電子報,都在這裡。

Mark Ku · 本文採用 CC BY 4.0 授權,轉載請註明作者並附上原文連結。

留言

訂閱電子報

訂閱後即時收到新文章通知,不錯過任何技術分享。

提交即表示同意接收電子報,隨時可

熱門文章

View all
Mark Ku
··603

Oracle Cloud 永久免費方案 Linux 主機及固定 IP :0 元打造雲端解決方案

Oracle Cloud 永久免費方案 Linux 主機及固定 IP :0 元打造雲端解決方案
Mark Ku
··440

告別 Postman 收費陷阱!開源 Git 原生 API 測試神器 Bruno 實戰指南

告別 Postman 收費陷阱!開源 Git 原生 API 測試神器 Bruno 實戰指南
Mark Ku
··323

一款免費開源類似於 Notion 類知識庫系統 — Outline Wiki 佈署與備份全攻略

一款免費開源類似於 Notion 類知識庫系統 — Outline Wiki 佈署與備份全攻略
Mark Ku
··239

打造高效 API 管理平台:從 0 開始部署 Kong Gateway - Part 1

打造高效 API 管理平台:從 0 開始部署 Kong Gateway - Part 1
Mark Ku
··222

在 Ubuntu 上設置 Samba 來共享資料夾,讓 Windows 11 用戶可以存取

在 Ubuntu 上設置 Samba 來共享資料夾,讓 Windows 11 用戶可以存取
Mark Ku
··199

訓練自己的 AI 語音:硬體門檻、開源模型比較與 LoRA 微調

訓練自己的 AI 語音:硬體門檻、開源模型比較與 LoRA 微調