四、Axios + Mock.js

1,452 阅读4分钟

前言

作为前后端分离的项目,必不可少的当然是发请求向后端拿数据了,由于一个人精力有限,我不打算前后端一起开发,主要原因是一起开发会导致,前端开发进度变慢,时间线拖长,会导致我本人激情的减退,最后可能这个项目就“太监”了。所以使用 mock.js 先模拟后端接口,等后面做后端开发的时候,可以无缝衔接,直接替换为真正的后端服务。

1. 引入 Aioxs

Axios 是一个基于 promise 的网络请求库,进一步了解参考官方文档:axios-http.com/zh/

1.1 安装依赖

npm install axios

1.2 封装请求工具类

新建目录 src/utils,新建文件 request.ts

import axios from 'axios'

const instance = axios.create({
  baseURL: '/api',
  timeout: 5000
})
// 添加请求拦截器
instance.interceptors.request.use(
  function (config) {
    // 请求成功做点什么
    return config
  },
  function (error) {
    // 对请求错误做点什么
    return Promise.reject(error)
  }
)
// 添加响应拦截器
instance.interceptors.response.use(
  function (response) {
    // 对响应成功做点什么
    return response
  },
  function (error) {
    // 对响应错误做点什么
    return Promise.reject(error)
  }
)

export default instance

2. 引入 Mock.js

生成随机数据,拦截 Ajax 请求。进一步了解参考官方文档:mockjs.com/

2.1 安装依赖

npm install --save-dev mockjs @types/mockjs

2.2 新建 src/mock 目录,新建 mock.ts

定义一个Mock获取用户信息的数据

import type { UserInfo } from '@/types/user'
import Mock from 'mockjs'

// 获取用户信息
Mock.mock('/api/user', 'get', () => {
  return {
    code: 200,
    success: true,
    message: '请求成功。',
    data: <UserInfo>{
      id: '1',
      username: 'admin',
      avatar: ''
    }
  }
})

2.3 在 main.ts 中引入

import './mock/mock.ts'

2.4 新建文件夹 src/api,新建文件 index.tsuser.ts

图片.png

这里用的是 ts 所以对于类型要求比较严格,所以要定义好我们生成数据的类型 新建 src/types 文件夹,data.d.ts 中我定义了统一的一些类型,user.d.ts 则是用户相关的类型 图片.png

//data.d.ts
// 所有的接口的通用类型
export interface ApiRes<T> {
  success: boolean
  code: string
  data?: T
  message: string
}

// 登录认证 token 信息
export interface Token {
  accessToken: string
}
// user.d.ts
// 用户信息
export interface UserInfo {
  id?: string
  username?: string
  avatar?: string
}

API接口定义

// api/user.ts
import type { UserInfo } from '@/types/user'

import request from '@/utils/request'
// 获取当前用户信息
export const getUserInfo = () => {
  return request<UserInfo>({
    method: 'GET',
    url: '/user'
  })
}
// api/index.ts
// 统一导出所有接口
import * as user from './user'

export default {
  user
}

3. 测试

App.vue 页面 加个 button 添加点击登录事件

<el-button type="primary" size="default" class="bg-green-500" @click="login">登录</el-button>
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import HelloWorld from './components/HelloWorld.vue'
import api from './api/index'

const login = () => {
  api.user.getUserInfo().then((res) => {
    console.log(res)
  })
}
</script>

启动项目点击测试

图片.png

可以看到,如我们所预期的那样拿到了结果。那么后面想要什么数据自己定义好接口返回就行了。

4. 一点点优化

我们想要拿到具体返回的内容,输入 res.data. 的时候由于 ts 类型检测给了我们提示,如果打印 res.data.id ,出来会是 undefined,因为从打印的 res 结果可以知道,其实我们要拿的数据应该在,res.data.data 里。

图片.png

图片.png

我们改成 res.data.data.id,成功取到值了,但是这个时候会发现 vscode 爆红了,提示找不到类型

图片.png

这里主要是因为,我们导出 axios 对象的时候,没有确定好类型,vscode 检测异常了, 而且在请求接口过程中,其实对于我们有用的信息就是最终的 data 对象,所以这里在封装的 request.ts 中,我们进行统一处理。

① 在响应拦截器中进行解构,返回 data 对象

// request.ts
// 添加响应拦截器
instance.interceptors.response.use(
  function (response) {
    const { success, message, data } = response.data
    // 判断当前请求是否成功
    if (success) {
      return data
    } else {
      // 请求失败,业务失败,消息提示
      return Promise.reject(new Error(message))
    }
  },
  function (error) {
    // 对响应错误做点什么
    return Promise.reject(error)
  }
)

② 再出 axios 对象的时候进行类型指定

// 指定返回数据类型,类型推断提示
export default <T = any>(config: AxiosRequestConfig) => {
  return instance(config).then((data) => {
    return data as T
  })
}

这个时候再去刚才测试的地方发现直接通过返回对象就可以有提示信息,并且可成功拿到返回结果。 如果发现没有提示或者爆红,重启一下 vscode

图片.png

思考

到这里基本配置已经可以了,但是这里我 baseURL 是写死的,'/api',我暂时打算通过这个来区分正式服务还是 mock 服务,如果是 /api 通过代理转到正式服务,如果是 /mock 则转到 mock 服务,然后通过环境配置文件.env.xxxx去动态配置 baseURL,但是这样还有一个问题就是,我想同时使用 mock 和 正式的服务,就没法弄了。其实最简单就是不要动 baseURL, 每一个接口定义两个,一个走正式,一个走 mock, 但是这样在环境切分上就要手动去改使用的方法,这是我不能接受的,暂时先留个坑,后面接入的时候再研究。

① 创建 .env.development,注意这里的变量名必须以 VITE_ 开头

# 标志
ENV = 'development'

# base api
VITE_APP_BASE_API = '/mock'

② 为我们自定义的变量添加类型扩展

// env.d.ts
// 扩展环境变量
interface ImportMetaEnv {
    readonly VITE_APP_TITLE: string
    readonly VITE_APP_PORT: number
    readonly VITE_APP_BASE_API: string
    // 更多环境变量...
  }
  
  interface ImportMeta {
    readonly env: ImportMetaEnv
  }

修改 request.ts 中的 baseURL,这时可以看到有提示。 图片.png 修改 mock.ts 中定义的接口,将前缀改成 /mock

补充

经过使用发现,这种方式不太方便调试,因为只有通过打印才能看到请求参数和请求返回的结果。已经做出调整,详细看这里,juejin.cn/post/724845…