Vue

2022.05.19

Vue 3 整合 VUEX 與使用 Axios 攔截器

前言

這兩天重構了 Vue 3 設定 axios 的攔截器,此外因為 vuex 也有打 API,所以重構的時候,也順便加入 vuex。

攔截器可以當成是一份能帶著走的 code snippet,所以把語法記在部落格裡,以後有開新專案就能照著參考啦 XD

2022.05.25:更新 development 以及 production 的相關設定

設定環境變數

我在開發的時候需要處理 API 的跨域問題,所以新增了兩個 .env 環境變數檔:

# 此為開發用的環境變數,檔名後面加上 .local 表示不會進 git
NODE_ENV=development
VUE_APP_API=/api
# 此為正式版用的環境變數
VUE_APP_API=https://API位置

如果使用 .local 結尾,就會被 git 忽略不會 push,所以我蠻推薦這個做法。以下是檔名與相應的介紹,資料來源為 Vue CLI 官方網站

.env                # 在所有的環境中被載入
.env.local          # 在所有的環境中被載入,但會被 git 忽略
.env.[mode]         # 只在指定的模式中被載入
.env.[mode].local   # 只在指定的模式中被載入,但會被 git 忽略

用代理 proxy 處理跨網域問題

我原本在 proxy 是直接設定 '/api' 這個網址去做 pathRewrite,但因為正式版會忽略 proxy 設定,導致我的 axios 網址會出現 '/api' 位置,因此我這邊有做一些調整,把 '/api' 改成環境變數去動態決定。

module.exports = {
  devServer: {
    proxy: {      
      [process.env.VUE_APP_API]: {
        target: API 位置,
        changeOrigin: true,
        secure: false,
        pathRewrite: {
          '^/api': ''
        }
      },
    }
  }
}

axios 目錄結構

- store/
  - index.js
- axios/
  - api.js   ## 管理 api
  - http.js  ## 設定攔截器

記得先安裝 axios 套件:

$ npm install axios

裝好 axios 套件後,我們需要做 axios 的攔截器,以及管理 api 網址,因此我會將目錄結構設計如下:

axios 攔截器

關於 token 的設計

專案的運作模式為,使用者登入確認後會得到一組 token,登入後的所有 API 行為都會帶這組 token,所以我把 token 寫在攔截器裡。

import axios from 'axios'

const $http = axios.create({
  baseURL: process.env.VUE_APP_API
})

// 攔截請求
$http.interceptors.request.use(config => {
  const token = localStorage.getItem('token')
  config.headers.Authorization = `Bearer ${token}`
  return config
}, error => {
  return Promise.reject(error)
})

// 攔截回應
$http.interceptors.response.use(res => {
  switch (res.status) {
    case 200:
      return Promise.resolve(res)
    default:
      console.log(res.status)
  }
}, error => {
  if (error && error.response) {
    switch (error.response.status) {
      case 400:
        console.error(error.response)
        break
      case 401:
        console.error(error)
        break
      default:
        console.error('攔截錯誤請求', error.response.status)
        return Promise.reject(error)
    }
  }
})

export default $http
Line 4: 引入 .env 設定的 API url
Line 9: 登入成功後從 Local Storage 取得 token

管理 api

因為 api 數量眾多,所以也集中在一個檔案統一管理 ( 如果數量不多,直接寫在 http.js,也是個不錯的方法 )

export default {
  article: {
    list: 'list/article',
    chart: 'execute/checkPost',
    location: 'list/article/1',
  },
  user: {
    list: 'list/user',
    detail: 'detail/user'
  }
}

使用 provide / inject 呼叫 API

provide 以及 inject 是 Vue 3.0 的新語法,可以取代 Vue.prototype 作為全域引入。

而除了 provide / inject 以外,也可以使用 globalProperties,不過官方好像比較推薦前者做法,有興趣深究的朋友可以深入了解官方 provide / inject 的分享

接著,我們就使用 provide, inject 來設定全域引入吧!

以 Vue Cli 為例,打開 main.js,匯入檔案後再使用 provide 傳遞資料:

import http from './axios/http'
import api from './axios/api'

app.provide('http', http)
app.provide('api', api)

接著開啟任一元件檔,用 inject 接收資料後,就能呼叫 API 了。

<script>
import { inject } from 'vue'

export default {
  setup () {
	const api = inject('api')
    const $http = inject('http')
    
    const getData = () => {
      $http.post(`/${api.article.list}`).then((res) => {
		console.log('hello world')
	  })
    }
  }
}
</script>

在 Vuex 使用 axios 攔截器

import $http from '@/axios/http'
import api from '@/axios/api'

export default createStore({
  actions: {
    getData (context, item) {
      $http.post(`/${api.article.list}/${item}`).then((res) => {
        console.log('hello world')
      })
    }
  }
})

覺得自己在用 Vuex 使用攔截器這部分,還沒有辦法處理得很精簡。

像是 $http 以及 api 檔案都需要再重新 import 一次,可能等以後經驗更足了再重新重構看看吧 XD。


以上,如果有任何問題,或是建議的寫法,都歡迎留言告訴我唷,謝謝!

歡迎給我點鼓勵,讓我知道你來過 :)

3
guest
2 則留言
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Harold
Harold
2 months ago

感覺 console.log(‘攔截錯誤請求’, error.response.status) 可以改成
console.error(‘攔截錯誤請求’, error.response.status)
會比較符合語意

Copyright (C) MUKI space* / Reborn Theme All Rights Reserved.