axios+mockjs+axios-mock-adapter实现本地数据mock

1,271 阅读3分钟

说明:axios负责发送请求,mockjs负责便捷构造响应数据,axios-mock-adapter负责代理axios数据请求到本地

很久没更新这篇文章了,目前新写了一篇,推荐这种方式:juejin.cn/post/734801…

目录结构:

图片.png

1.安装axios+mockjs+axios-mock-adapter

npm i axios mockjs axios-mock-adapter

2.封装axios

import axios from 'axios'
import type {
  AxiosInstance,
  AxiosError,
  AxiosRequestConfig,
  AxiosResponse,
} from 'axios'
import { ElMessage } from 'element-plus'
import { useUserStore } from '@/store/modules/user'
import { ResultEnum } from '@/enums/httpEnums'
import { ResultData } from './type'
import { LOGIN_URL } from '@/config/config'
import { RESEETSTORE } from '../reset'
import router from '@/router'
import { initMock } from '@/mock/mock'
export enum ResultEnum {
  SUCCESS = 200,
  EXPIRE = 203,
  ERROR = -1,
  ERRMESSAGE = '请求失败',
  TIMEOUT = 25000,
  TYPE = 'success',
}
export const service: AxiosInstance = axios.create({
  // 判断环境设置不同的baseURL
  baseURL:
    import.meta.env.NODE_ENV === 'development'
      ? import.meta.env.VITE_APP_BASE_API
      : import.meta.env.VITE_APP_BASE_URL,
  timeout: ResultEnum.TIMEOUT as number,
})
// 根据配置决定是否使用mock
if (import.meta.env?.VITE_APP_OPEN_MOCK?.toString() === 'true') {
  // 初始化mock,axios-mock-adapter
  console.log('初始化mock,axios-mock-adapter')
  initMock(service)
}
​
/**
 * @description: 请求拦截器
 * @returns {*}
 */
service.interceptors.request.use(
  (config) => {
    const userStore = useUserStore()
    const token = userStore.token
    if (token) {
      config.headers.token = token
    }
    return config
  },
  (error: AxiosError) => {
    ElMessage.error(error.message)
    return Promise.reject(error)
  },
)
/**
 * @description: 响应拦截器
 * @returns {*}
 */
service.interceptors.response.use(
  (response: AxiosResponse) => {
    const { data } = response
    // * 登陆失效(code == 203)
    if (data.code === ResultEnum.EXPIRE) {
      RESEETSTORE()
      ElMessage.error(data.message || ResultEnum.ERRMESSAGE)
      router.replace(LOGIN_URL)
      return Promise.reject(data)
    }
​
    if (data.code && data.code !== ResultEnum.SUCCESS) {
      ElMessage.error(data.message || ResultEnum.ERRMESSAGE)
      return Promise.reject(data)
    }
    return data
  },
  (error: AxiosError) => {
    // 处理 HTTP 网络错误
    let message = ''
    // HTTP 状态码
    const status = error.response?.status
    switch (status) {
      case 401:
        message = 'token 失效,请重新登录'
        break
      case 403:
        message = '拒绝访问'
        break
      case 404:
        message = '请求地址错误'
        break
      case 500:
        message = '服务器故障'
        break
      default:
        message = '网络连接故障'
    }
​
    ElMessage.error(message)
    return Promise.reject(error)
  },
)
​
/**
 * @description: 导出封装的请求方法
 * @returns {*}
 */
const http = {
  get<T>(
    url: string,
    params?: object,
    config?: AxiosRequestConfig,
  ): Promise<ResultData<T>> {
    return service.get(url, { params, ...config })
  },
​
  post<T>(
    url: string,
    data?: object,
    config?: AxiosRequestConfig,
  ): Promise<ResultData<T>> {
    return service.post(url, data, config)
  },
​
  put<T>(
    url: string,
    data?: object,
    config?: AxiosRequestConfig,
  ): Promise<ResultData<T>> {
    return service.put(url, data, config)
  },
​
  delete<T>(
    url: string,
    data?: object,
    config?: AxiosRequestConfig,
  ): Promise<ResultData<T>> {
    return service.delete(url, { data, ...config })
  },
}
​
export default http
​

3.具体实现代码

1.mock.ts

import MockAdapter from 'axios-mock-adapter'
import Mock from 'mockjs'
import { AxiosInstance } from 'axios'
import { resultSuccess } from '@/mock/_utils'
// 引入所有的mock/api中的文件 匹配所有以.ts结尾的文件,但不包括文件名包含"resInfo"的文件
const modules = import.meta.glob('@/mock/api/**/!(*resInfo).ts')
let mock: MockAdapter | null
/**
 * @description: 初始化mock
 * @param axiosInstance
 * @param delayResponse
 */
export function initMock(axiosInstance: AxiosInstance, delayResponse = 1000) {
  // 注册所有modules中的mock
  mock = new MockAdapter(axiosInstance, { delayResponse })
  Object.keys(modules).forEach((key) => modules[key]())
  return mock
}
​
/**
 * @description: 使用mock
 */
export function useMock() {
  if (!mock) {
    throw new Error('Mock not initialized')
  } else {
    return mock
  }
}
​
/**
 * @description: 使用mock onAny
 */
export function useMockOnAny() {
  if (!mock) {
    throw new Error('Mock not initialized')
  } else {
    return (url: string, data: any) => {
      return mock?.onAny(url).reply(() => {
        return new Promise((resolve) => {
          console.log(url, resultSuccess(data))
          resolve([200, resultSuccess(data)])
        })
      })
    }
  }
}
/**
 * @description: 使用Mock来模拟数据
 * @param data
 */
export function constructMockData<T>(data: T): T {
  return Mock.mock(data)
}
​

2._utils.ts

/*
 * @Author: cqq 945327638@qq.com
 * @Date: 2023-06-02 10:58:21
 * @LastEditors: cqq 945327638@qq.com
 * @LastEditTime: 2023-06-02 18:00:01
 * @FilePath: \InvestmentAdmin\src\mock_utils.ts
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
import { ResultEnum } from '@/enums/httpEnums'/**
 * @description: 错误响应结构
 * @returns {*}
 */
export function resultError(
  message = 'Request failed',
  { code = ResultEnum.ERROR, data = null } = {},
) {
  return {
    code,
    data,
    message,
    type: 'error',
  }
}
​
/**
 * @description: 成功响应结构
 * @returns {*}
 */
export function resultSuccess<T>(data: T, { message = 'ok' } = {}) {
  return {
    code: ResultEnum.SUCCESS,
    data,
    message,
    type: 'success',
  }
}
​
export interface requestParams {
  method: string
  body: any
  headers?: { authorization?: string }
  query: any
}
​
/**
 * @description 本函数用于从request数据中获取token,请根据项目的实际情况修改
 * @return token
 */
export function getRequestToken({
  headers,
}: requestParams): string | undefined {
  return headers?.authorization
}
​

3.api/user/user

import { useMockOnAny } from '@/mock/mock'
import { loginInfoRes, userInfoRes } from '@/mock/api/user/resInfo'
const mockOnAny = useMockOnAny()
console.log('我被执行了')
// 登陆
mockOnAny('/admin/system/securityLogin/login', loginInfoRes)
// 获取用户信息
mockOnAny('/admin/system/index/info', userInfoRes)
// 退出登陆
mockOnAny('/admin/system/index/logout', {})
​

4.api/user/resInfo

import { constructMockData } from '@/mock/mock'// 登陆信息
export const loginInfoRes = constructMockData({
  token: 'eyJhbGciOiJIUzUxMiIsInppcCI6IkdaSVAifQ',
})
// 用户信息
export const userInfoRes = constructMockData({
  buttons: [],
  roles: [],
  name: '@name',// 使用mock语法
  avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
  routers: [],
})
​
​

4.使用,直接使用axios发送即可

import http from '@/utils/http'
import type { LoginData, UserRes } from './types'
/* 登录接口参数类型 */
export interface LoginData {
  username: string
  password: string
}
​
export interface UserRes {
  userId?: string
  name: string
  avatar: string
  buttons: string[]
  roles: string[]
  routers: Menu.MenuOptions[]
}
/** 用户权限数据类型 */
export interface AuthInfo {
  buttons: string[]
  roles: string[]
  routers: Menu.MenuOptions[]
}
​
/* 用户信息接口返回值类型 */
export interface UserInfo {
  userId?: string
  name: string
  avatar: string
}
​
/**
 * 登录
 */
export function login(data: LoginData) {
  return http.post<{ token: string }>('/admin/system/securityLogin/login', data)
}
​
/**
 * 获取登录用户信息
 */
export function getUserInfo() {
  return http.get<UserRes>('/admin/system/index/info')
}
​
/**
 * 退出登陆
 */
export function logout() {
  return http.post('/admin/system/index/logout')
}
​