下面仅演示了 TS 中 axios 封装的基础版本(axios 基础封装 + 响应结果类型处理),更多版本可以查看个人项目:

https://github.com/Lencamo/request-tools

提示

  在正式的开发中,Axios 请求封装往往伴随着 状态管理工具 的使用。

# 一、JS 环境中

# 1、结构

├── services
    ├── index.js
    ├── request # axios封装
    │   ├── index.js
    │   └── config.js
    └── modules # api管理
        ├── cart.js
        └── products.js
// @/services/index.js

import RenRequest from './request/index.js'
import { BASE_URL, TIMEOUT } from './request/config.js'

export const shopRequest = new RenRequest({
  baseURL: BASE_URL,
  timeout: TIMEOUT
})

export * from './modules/cart.js'

# 2、axios 封装

// @/services/request/config.js

export const BASE_URL = 'http://xxxxx.com'
export const TIMEOUT = 10000
// @/services/request/index.js

import axios from 'axios'

class RenRequest {
  constructor(config) {
    this.instance = axios.create(config)

    // 响应拦截
    this.instance.interceptors.response.use(
      (response) => {
        return response.data
      },
      (error) => {
        return error
      }
    )
  }

  request(config) {
    return this.instance.request(config)
  }

  get(config) {
    return this.request({ ...config, method: 'get' })
  }

  post(config) {
    return this.request({ ...config, method: 'post' })
  }
}

export default RenRequest

# 3、api 管理

// @/services/modules/cart.js

import { shopRequest } from '../index.js'

export function getCartList(offset = 0, size = 20) {
  return shopRequest.get({
    url: 'carts/list',
    params: {
      offset,
      size
    }
  })
}

# 4、使用示例(Vuex)

  • vuex 中
vuex 模块化的目录结构
├── store
    ├── index.js
    ├── getters.js        # 根级别的 getters
    ├── actions.js        # 根级别的 action
    ├── mutations.js      # 根级别的 mutation
    └── modules
        ├── cart.js
        └── products.js
// @/store/modules/cart.js
import { getCartList } from '@/services/modules/cart'

const state = {
  cartList: []
}

const mutations = {
  changeCartList(state, payload) {
    state.cartList = payload.data
  }
}

// 方式1
const actions = {
  fetchCartListAction({ commit }, PageInfo) {
    const { offset, size } = PageInfo

    return new Promise((resolve) => {
      getCartList(offset, size)
        .then((res) => {
          commit('changeCartList', { data: res.data }) // 修改state(在vuex中需要使用Mutations修改/更新数据)
          resolve('修改state成功了')
        })
        .catch((error) => {
          reject(error)
        })
    })
  }
}

// 方式2
const actions = {
  async fetchCartListAction({ commit }, PageInfo) {
    const { page } = PageInfo

    try {
      const res = await getCartList(page * 20)
      commit('changeCartList') // 修改state(在vuex中需要使用Mutations修改/更新数据)
    } catch (err) {
      console.log(err)
    }
  }
}

export default {
  // 开启命名空间
  namespaced: true,
  state,
  mutations,
  actions
}
  • 组件应用中
<script>
import { mapActions } from 'vuex'

export default {
  methods: {
    ...mapAction('cart', ['fetchCartListAction'])

    // ======================

    // fetchCartListAction() {
    //   this.$store.dispatch('cart/fetchCartListAction')
    // }
  }
}
</script>

# 二、TS 环境中

# 1、结构

├── services
    ├── index.ts
    ├── request # axios封装
    │   ├── index.ts
    │   └── config.ts
    └── modules # api管理
        ├── cart.ts
        └── products.ts
// @/services/index.ts

import RenRequest from './request/index'
import { BASE_URL, TIMEOUT } from './request/config'

export const testRequest = new RenRequest({
  baseURL: BASE_URL,
  timeout: TIMEOUT
})

export * from './modules/home'

# 2、axios 封装

// @/services/request/config.ts

export const BASE_URL = 'http://xxxxx.com'
export const TIMEOUT = 10000
// @/services/request/index.ts

import axios from 'axios'
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'

class RenRequest {
  instance: AxiosInstance

  constructor(config: AxiosRequestConfig) {
    this.instance = axios.create(config)

    // 响应拦截
    this.instance.interceptors.response.use(
      (response: AxiosResponse) => {
        return response
      },
      (error: any) => {
        return error
      }
    )
  }

  request(config: AxiosRequestConfig): Promise<AxiosResponse> {
    return this.instance.request(config)
  }

  get<T = any>(config: AxiosRequestConfig): Promise<AxiosResponse<T>> {
    return this.request({ ...config, method: 'get' })
  }

  post<T = any>(config: AxiosRequestConfig): Promise<AxiosResponse<T>> {
    return this.request({ ...config, method: 'post' })
  }
}

export default RenRequest

# 3、api 管理

// @/services/modules/cart.ts

import { testRequest } from '../index'
import type { AxiosResponse } from 'axios'

/**
 * response数据类型
 * - 【默认为any】
 */

interface IHomeData {
  data: any
  returnCode: string
  success: boolean
}

export function getHomeList(
  offset: number = 0,
  size: number = 20
): Promise<AxiosResponse<IHomeData, any>> {
  return testRequest.get<IHomeData>({
    url: 'carts/list',
    params: {
      offset,
      size
    }
  })
}

# 4、使用示例(Pinia)

  • Pinia 中
Pinia 模块化的目录结构

  Pinia 中的每一个文件就是一个单独的 store,所以不存在什么模块划分的说法。

├── stores
    ├── index.js
    ├── cart.js
    └── products.js
// @/store/cart.js
import { getCartList } from '@/services/modules/cart'

export const useCounter = defineStore('main', {
  state() {
    return {
      cartList: []
    }
  },
  actions: {
    // 方式1
    fetchCartListAction(payload) {
      const { offset, size } = payload

      return new Promise((resolve) => {
        getCartList(offset, size)
          .then((res) => {
            this.cartList = res.data // 可以直接修改state啦😘(再也不要像vuex那样commit、像redux那样和普通action区分了)
            resolve('修改state成功了')
          })
          .catch((error) => {
            reject(error)
          })
      })
    },

    // 方式2
    async fetchCartListAction(payload) {
      const { offset, size } = payload

      try {
        const res = await getCartList(offset, size)
        this.cartList = res.data // 可以直接修改state啦😘(再也不要像vuex那样commit、像redux那样和普通action区分了)
      } catch (err) {
        console.log(err)
      }
    }
  }
})
  • 组件应用中
<script setup>
  import { useCart } from '@/stores/cart'

  const cartStore = useCart()

  // 使用
  // cartStore.fetchCartListAction()
</script>
更新于 : 7/8/2024, 10:21:14 AM