首页 > 其他分享 >Vite4+Typescript+Vue3+Pinia 从零搭建(7) - request封装

Vite4+Typescript+Vue3+Pinia 从零搭建(7) - request封装

时间:2023-12-20 18:46:48浏览次数:27  
标签:Typescript return object request ts Pinia message config

项目代码同步至码云 weiz-vue3-template
基于 axios 封装请求,支持多域名请求地址

安装

npm i axios

封装

utils 目录下新建 request 文件夹,并新建 index.tsrequest.tsstatus.ts 文件。

1. status.ts 文件主要是封装状态码

export const ErrMessage = (status: number | string): string => {
  let message: string = ''
  switch (status) {
    case 400:
      message = '请求错误!请您稍后重试'
      break
    case 401:
      message = '未授权!请您重新登录'
      break
    case 403:
      message = '当前账号无访问权限!'
      break
    case 404:
      message = '访问的资源不存在!请您稍后重试'
      break
    case 405:
      message = '请求方式错误!请您稍后重试'
      break
    case 408:
      message = '请求超时!请您稍后重试'
      break
    case 500:
      message = '服务异常!请您稍后重试'
      break
    case 501:
      message = '不支持此请求!请您稍后重试'
      break
    case 502:
      message = '网关错误!请您稍后重试'
      break
    case 503:
      message = '服务不可用!请您稍后重试'
      break
    case 504:
      message = '网关超时!请您稍后重试'
      break
    default:
      message = '请求失败!请您稍后重试'
  }
  return message
}

此时,eslint会报 switch 前面的空格错误,需要修改 .eslintrc.cjs 里的 indent,修改后,错误消失。

rules: {
  // Switch语句 https://zh-hans.eslint.org/docs/latest/rules/indent#switchcase
  indent: ['error', 2, { SwitchCase: 1 }]
}

2. request.ts 主要是封装 axios

/**
 * 封装axios
 * axios 实例的类型为 AxiosInstance,请求需要传入的参数类型为 AxiosRequestConfig,响应的数据类型为 AxiosResponse,InternalAxiosRequestConfig 继承于 AxiosRequestConfig
 */
import axios, { AxiosInstance, AxiosRequestConfig, InternalAxiosRequestConfig, AxiosResponse } from 'axios'
import { ErrMessage } from './status'

// 自定义请求返回数据的类型
interface Data<T> {
  data: T
  code: string
  success: boolean
}

// 扩展 InternalAxiosRequestConfig,让每个请求都可以控制是否要loading
interface RequestInternalAxiosRequestConfig extends InternalAxiosRequestConfig {
  showLoading?: boolean
}

// 拦截器
interface InterceptorHooks {
  requestInterceptor?: (config: RequestInternalAxiosRequestConfig) => RequestInternalAxiosRequestConfig
  requestInterceptorCatch?: (error: any) => any
  responseInterceptor?: (response: AxiosResponse) => AxiosResponse
  responseInterceptorCatch?: (error: any) => any
}
// 扩展 AxiosRequestConfig,showLoading 给实例默认增加loading,interceptorHooks 拦截
interface RequestConfig extends AxiosRequestConfig {
  showLoading?: boolean
  interceptorHooks?: InterceptorHooks
}

class Request {
  config: RequestConfig
  instance: AxiosInstance
  loading?: boolean // 用loading指代加载动画状态

  constructor(options: RequestConfig) {
    this.config = options
    this.instance = axios.create(options)
    this.setupInterceptor()
  }

  // 类型参数的作用,T决定AxiosResponse实例中data的类型
  request<T = any>(config: RequestConfig): Promise<T> {
    return new Promise((resolve, reject) => {
      this.instance
        .request<any, Data<T>>(config)
        .then((res) => {
          resolve(res.data)
        })
        .catch((err) => {
          reject(err)
        })
    })
  }

  // 封装常用方法
  get<T = any>(url: string, params?: object, _object = {}): Promise<T> {
    return this.request({ url, params, ..._object, method: 'GET' })
  }

  post<T = any>(url: string, params?: object, _object = {}): Promise<T> {
    return this.request({ url, params, ..._object, method: 'POST' })
  }

  delete<T = any>(url: string, params?: object, _object = {}): Promise<T> {
    return this.request({ url, params, ..._object, method: 'DELETE' })
  }

  patch<T = any>(url: string, params?: object, _object = {}): Promise<T> {
    return this.request({ url, params, ..._object, method: 'PATCH' })
  }

  put<T = any>(url: string, params?: object, _object = {}): Promise<T> {
    return this.request({ url, params, ..._object, method: 'PUT' })
  }

  // 自定义拦截器 https://axios-http.com/zh/docs/interceptors
  setupInterceptor(): void {
    /**
     * 通用拦截
     */
    this.instance.interceptors.request.use((config: RequestInternalAxiosRequestConfig) => {
      if (config.showLoading) {
        // 加载loading动画
        this.loading = true
      }
      return config
    })
    // 响应后关闭loading
    this.instance.interceptors.response.use(
      (res) => {
        if (this.loading) this.loading = false
        return res
      },
      (err) => {
        const { response, message } = err
        if (this.loading) this.loading = false
        // 根据不同状态码,返回不同信息
        const messageStr = response ? ErrMessage(response.status) : message || '请求失败,请重试'
        window.alert(messageStr)
        return Promise.reject(err)
      }
    )
    /**
     * 使用通用实例里的拦截,两个拦截都会生效,返回值以后一个执行的为准
     */
    // 请求拦截
    this.instance.interceptors.request.use(
      this.config?.interceptorHooks?.requestInterceptor,
      this.config?.interceptorHooks?.requestInterceptorCatch
    )
    // 响应拦截
    this.instance.interceptors.response.use(
      this.config?.interceptorHooks?.responseInterceptor,
      this.config?.interceptorHooks?.responseInterceptorCatch
    )
  }
}

export default Request

3. index.ts 主要是创建 Request 实例

/**
 * 创建实例,可以多个,当你需要请求多个不同域名的接口时
 */
import Request from './request'
import { getToken } from '@/utils/auth'

const defRequest = new Request({
  // 这里用 Easy Mock 模拟了真实接口
  baseURL: 'https://mock.mengxuegu.com/mock/65421527a6dde808a695e96d/official/',
  timeout: 5000,
  showLoading: true,
  interceptorHooks: {
    requestInterceptor: (config) => {
      const token = getToken()
      if (token) {
        config.headers.Authorization = token
      }
      return config
    },
    requestInterceptorCatch: (err) => {
      return err
    },
    responseInterceptor: (res) => {
      return res.data
    },
    responseInterceptorCatch: (err) => {
      return Promise.reject(err)
    }
  }
})

// 创建其他示例,然后导出
// const otherRequest = new Request({...})

export { defRequest }

使用

src 目录下新建 api 文件夹,并新建 login.ts

1. login.ts

import { defRequest } from '../utils/request'

export const loginApi = (params: any) => {
  // 设置 showLoading,timeout 会覆盖index.ts里的默认值
  return defRequest.post<any>('/login', params, { showLoading: false, timeout: 1000 })
}

2. 修改 login.vue

<script setup lang="ts">
import { ref } from 'vue'
import { storeToRefs } from 'pinia'
import { useUserStore } from '@store/user'
import { loginApi } from '@/api/login'

defineOptions({
  name: 'V-login'
})

const userStore = useUserStore()
const { userInfo, token } = storeToRefs(userStore)
let userName = ref(userInfo.value.name)
let userToken = ref(token)

const updateUserName = () => {
  userStore.setUserInfo({
    name: userName.value
  })
}
const updateUserToken = () => {
  userStore.setToken(userToken.value)
}

const login = () => {
  loginApi({
    name: userName.value
  })
    .then((res) => {
      userName.value = res.name
      userToken.value = res.token
      updateUserToken()
    })
    .catch((err) => {
      console.log(err)
    })
}
</script>

<template>
  <div>login page</div>
  name:
  <input type="text" v-model="userName" @input="updateUserName" />
  <br />
  token:
  <input type="text" v-model="userToken" />
  <hr />
  <button @click="login">login</button>
</template>

<style scoped></style>

点击 login 按钮,即可看到请求。
image

说明

对于 axios 的封装和使用,这里要说明几点:

1. 为什么要使用 InternalAxiosRequestConfig

axios 源码有修改,拦截器传入和返回的参数不再是 AxiosRequestConfig,而是这个新类型 InternalAxiosRequestConfig
想要具体了解,可以查看这篇博文 https://blog.csdn.net/huangfengnt/article/details/131490913

2. Request 里的 config 参数

constructor 里的 this.config 会接受所有实例参数,所以通用实例拦截里使用的是 this.config?.xxx
通用拦截里使用的是 config.showLoading,而不是 this.config.showLoading,是为了我们在实际的 api/login.ts 里可以再传入 showLoading,以满足我们单个请求的要求。而通过 this.config 里获取的配置是 request/index.ts 里传入的配置。在 config.showLoading 之前我们可以打印下这两个 configconsole.log(this.config, config) 结果如下:
image

如果在 login.ts 里不传入 showLoading,那么 config.showLoading 会去拿通用实例 request/index.ts 里的 showLoading
** 当然如果不需要全局加载动画,整个 loading 也都可以去掉 **

3. 总结下 request/index.tsapi/login.ts 里的参数有什么不同

request/index.ts 里可以建多个实例,一般以 baseURL 来判断是否要多个,它的参数是当前url下的通用参数,拦截规则也是;
api/login.ts 是具体的请求,它的大部分参数是url和请求传参。同一个 baseURL 下有的请求有特殊的要求,那你就可以去加一些参数。
总的来说,request/index.ts 是对 baseURL 一样的请求的封装,request/request.ts 是对所有请求的封装

4. 优化

  • 因为 Easy Mock 的接口支持跨域,所以没有配到代理里去,如果是正常开发接口,还需要修改 vite.config.ts 里的 proxy。不过我们之前的教程里已有代理配置说明,这里便不再赘述
  • baseURL 还可以放在 env 变量里,以便区分开发环境和生产环境
  • ** 删除 loading,这里只是为了提供一种思路

    标签:Typescript,return,object,request,ts,Pinia,message,config
    From: https://www.cnblogs.com/weizwz/p/17917222.html

相关文章

  • requests入门
    安装Requestspipinstallrequests发送请求接口:https://api.github.com/events获取接口信息r=requests.get('https://api.github.com/events')之后获取的信息都是从r对象来的其他的请求类型:#post类型r=requests.post('http://httpbin.org/post',data={'key':'......
  • Python(requests.post()、requests.get())
    目录1.requests.post()2.requests.get()3.requests.post()与requests.get()区别1.requests.post()requests.post是Python中requests库提供的一个函数,用于发送HTTPPOST请求。这个函数的基本语法如下:importrequestsresponse=requests.post(url,data=None,j......
  • Python的Requests库与网页爬取
    requests库的几种方法 其他几个方法内部实际都调用了requests.request()方法 Response对象的属性 首先要使用r.status_code判断连接是否成功。 Request库的异常  爬取网页的通用代码  需要处理异常,使爬取网页变得更有效、可靠、稳定。  HTTP  无......
  • Request+Python微博爬虫实战
    1Request爬虫基础Request爬虫基本步骤:1、构造URL;2、请求数据;3、解析数据;4、保存数据例:爬取豆瓣某图片importrequests#第1步:构造URLurl='https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2624516210.jpg'#第2步:请求数据r=requests.get(url)#第3步:解......
  • 【UniApp】-uni-app-pinia存储数据
    前言经过上个章节的介绍,大家可以了解到uni-app-数据缓存的基本使用方法那本章节来给大家介绍一下uni-app-pinia存储数据的基本使用方法经过我这么多篇章的介绍,我发现大家环境比较耗时,所以在今后的文章中,我会尽量减少环境的搭建如果某一篇的文章环境确实是不一样的,我会在......
  • 第三章:@RequestMapping注解
    一、搭建框架二、控制器中有多个方法对应同一个请求的情况三、@RequestMapping注解标识的位置四、@RequestMapping注解的value属性五、@RequestMapping注解的method属性六、@RequestMapping注解结合请求方式的派生注......
  • 这10个TypeScript高级技巧,助你成为更好的开发者!
    这10个TypeScript高级技巧,助你成为更好的开发者!前端学习站 ​关注他 在使用了一段时间的Typescript之后,我深深地感受到了Typescript在大中型项目中的必要性。可以提前避免很多编译期的bug,比如烦人的拼写问题。并且越来越多的包都在使用TS,所以学习它势在必行......
  • 关于Pinia 使用setup方式书写 $reset方法失效问题
    在当我使用的时候踩到一个坑:当我在使用$reset想要重置state数据的时候,却报错了,经过排查发现是因为没有使用选项式进行编写代码关于$reset方法Pinia文档中只有简短的介绍:您可以通过调用store上的$reset()方法将状态重置到其初始值:conststore=useStore()store.$res......
  • pinia初学习
    pinia两种写法定义pinia第一种:对象形式不需要写refstate直接就是响应式数据import{defineStore}from"pinia"exportconstuseCounterStore=defineStore("useCounterStore",{state:()=>{return{}},actions:{......
  • Python自动化软件测试:接口测试Requests模块从0到精通
    Requests模块是Python中一个非常流行的第三方库,用于处理HTTP请求。在接口自动化测试中,Requests模块可用于模拟发送HTTP请求并检查响应数据,以验证API的功能和性能。以下是与Requests模块相关的一些知识点,这些知识点都是从事接口自动化测试,必须要掌握的内容:安装和导入Requests模块:使......