vue3实战教程之axios的封装和环境变量

axios

axios: ajax i/o system. 一个可以同时在浏览器和node环境进行网络请求的第三方库

功能特点:

  • 在浏览器中发送 XMLHttpRequests 请求
  • 在 node.js 中发送 http请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求和响应数据
  • 等等

基本使用

get请求

// 导入的axios是一个实例对象
import axios from 'axios'

// axios方法返回的是promise
const res = await axios.get('https://httpbin.org/get', {
 	// 传递query参数
  params: {
    name: 'Klaus'
  }
})

// 服务器实际返回的数据被放置在data属性中
console.log(res.data)

post请求

const res = await axios.post('https://httpbin.org/post', {
  data: {
    name: 'Klaus'
  }
})

request请求

// axios的所有请求本质上都是在调用axios的request方法
const res = await axios.request({
  url: 'https://httpbin.org/post',
  method: 'post',
  data: {
    name: 'Klaus'
  }
})

all

axios.all本质上就是Promise.all

const res = await axios.all([
  axios.get('https://httpbin.org/get', {
    params: {
      name: 'Klaus'
    }
  }),

  axios.post('https://httpbin.org/post', {
    data: {
      name: 'Klaus'
    }
  })
])

拦截器

// 拦截器必须要在请求和响应之前被注册

// 请求拦截器 --- use方法的两个参数 --- 分别对应resolve和reject
// resolve方法的参数为请求的配置选项
// reject方法的参数为错误对象
axios.interceptors.request.use(
  config => {
    console.log(config)
    return config
  },
  err => console.error(err)
)

// 响应拦截器 --- use方法的两个参数 --- 分别对应resolve和reject
// resolve方法的参数为响应体
// reject方法的参数为错误对象
axios.interceptors.response.use(
  res => {
    return res.data
  },
  err => console.error(err)
)

配置

// 以下都是全局配置
// axios的所有全局配置都可以设置在属性defaults下

// 基本请求公共路径
axios.defaults.baseURL = 'http://httpbin.org'

// 超时时间
axios.defaults.timeout = 10000

// 需要传递的自定义请求头
axios.defaults.headers = {}
// 局部配置
axios
  .get('/get', {
  params: {
    name: 'Klaus',
    age: 18
  },
  
  // 局部配置可以覆盖全局配置
  timeout: 5000,
  headers: {}
})
  .then((res) => {
  console.log(res.data)
})

封装

如果直接在每一个需要进行网络请求的页面中的引入axios会导致如下问题:

  • 每一个页面都需要引入axios, 就导致axios和逻辑的耦合性过强,如果以后需要进行修改,那么就需要去每一个使用了axios的页面中进行修改

    是十分麻烦且容易出错的

  • 在页面请求的时候,我们会传递许多公共配置,例如BASE_URL,TOKEN等。

所以我们需要对我们的网络请求进行二次封装 --- 一般会放置在src下的service或者api文件夹下

type.ts

import { AxiosRequestConfig, AxiosResponse  } from 'axios'

// 自定义自己的拦截器类型
export interface Interceptor {
  requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig | Promise,
  requestInterceptorCatch?: (err: any) => any,
  responseInterceptor?: (res: AxiosResponse) => AxiosResponse | Promise,
  responseInterceptorCatch?: (err: any) => any,
}

// 通过接口继承的方式扩展axios的AxiosRequestConfig类型
export interface Config extends AxiosRequestConfig{
  interceptor?: Interceptor
}

env.ts

// 将一些相关配置配置成常量后在引入使用
// 将配置和调用解耦,如果需要修改,直接修改常量的值即可
export const TIME_OUT = 10000
export const BASE_URL = 'https://httpbin.org/'

index.ts

import Api from './api'

import {
  BASE_URL,
  TIME_OUT
} from './env'

const api = new Api({
  baseURL: BASE_URL,
  timeout: TIME_OUT,

  // 不同的实例可能有不同的拦截器
  // 所以我们将拦截器封装成一个扩展属性进行传入
  // interceptor: {
  //   requestInterceptor: config => {
  //     console.log('请求成功')
  //     return config
  //   },
  //   requestInterceptorCatch: err => console.error(err),
  //   responseInterceptor: res => res.data,
  //   responseInterceptorCatch:  err => console.error(err)
  // }
})

export default api

api.ts

import axios from 'axios'
import { ElLoading } from 'element-plus'
import type { AxiosInstance, AxiosRequestConfig } from 'axios'
import type { Config } from './type'

// 导出的属性和方法比较多 所以使用类来进行封装
// 因为类具有比较好的封装性
export default class {
  instance: AxiosInstance

  constructor(config: Config) {
    // 每次创建实例的时候,都调用axios.create方法
    // axios.create可以返回一个axios实例
    // 这样保证我们可以使用不同的配置创建多个axios实例
    this.instance = axios.create(config)

    // 如果存在实例级别的拦截器 就使用拦截器
    // 这是针对于每一个请求特有的拦截器 --- 实例拦截
    // 这里的操作也可以通过transformRequest和transformResponse配置项来进行实现
    if (config.interceptor) {
      const { interceptor } = config
      this.instance.interceptors.request.use(interceptor.requestInterceptor, interceptor.requestInterceptorCatch)
      this.instance.interceptors.response.use(interceptor.responseInterceptor, interceptor.responseInterceptorCatch)
    }

    this.registerGlobalInterceptor(config)
  }

  // 这是所有实例共有的拦截器 --- 全局拦截
  // 如果存在多个拦截器,那么多个拦截器都会被执行
  registerGlobalInterceptor(option: Config) {
    this.instance.interceptors.request.use(config => config, err => err)
    this.instance.interceptors.response.use(res => res.data, err => err)
  }

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

请求时添加loading

import axios from 'axios'
import { ElLoading } from 'element-plus'
import type { AxiosInstance, AxiosRequestConfig } from 'axios'
import type { LoadingInstance } from 'element-plus/es/components/loading/src/loading'
import type { Config } from './type'

// el-loading是插件不是组件,element-plus的按需引入并不能正确的引入el-loading的样式
// 所以需要自己手动进行引入el-loading的样式
import 'element-plus/theme-chalk/el-loading.css'

export default class {
  instance: AxiosInstance
  loading: LoadingInstance | undefined

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

    if (config.interceptor) {
      const { interceptor } = config
      this.instance.interceptors.request.use(interceptor.requestInterceptor, interceptor.requestInterceptorCatch)
      this.instance.interceptors.response.use(interceptor.responseInterceptor, interceptor.responseInterceptorCatch)
    }

    this.registerGlobalInterceptor()
  }

  registerGlobalInterceptor() {
    this.instance.interceptors.request.use((config: Config) => {
      // ?? --- 当左侧的操作数为 null 或者 undefined 时,返回右侧操作数,否则返回左侧操作数
      if (config?.showLoading ?? true) {
        // 开启loading
        this.loading =  ElLoading.service({
          lock: true,
          text: 'Loading...',
          background: 'rgba(0, 0, 0, 0.7)',
        })
      }

      return config
    }, err => err)

    this.instance.interceptors.response.use(res => {
      // 关闭loading
      this.loading?.close()

      // axios返回的是字符串,所以需要反序列化
      return JSON.parse(res.data)
    }, err => {
      this.loading?.close()
      return err
    })
  }

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

环境变量

在开发中,有时候我们需要根据不同的环境设置不同的环境变量,常见的有四种环境:

  • 开发环境: development
  • 生产环境: production
  • 测试环境: test
  • 预发布环境: stage

配置方式1: 手动的切换不同的环境(不推荐)

// 开发环境
// const BASE_URL = 'http://example.org/dev'
// const BASE_NAME = 'Klaus'

// 生产环境
// const BASE_URL = 'http://example.org/prod'
// const BASE_NAME = 'Alex'

// 测试环境
// const BASE_URL = 'http://example.org/test'
// const BASE_NAME = 'Steven'

配置方式2 --- 根据process.env.NODE_ENV的值进行区分

// vite默认会设置一下自带的环境变量
// 如  MODE, PROD, DEV, SSR, BASE_URL等
if (import.meta.env.MODE === 'development') {
 // ... do something
} else {
 // ... do else
}

配置方式3 --- 编写不同的环境变量配置文件

# .env.production
# 注意: 在vite中所有的环境变量必须以VITE_开头
VITE_APP_TITLE=My App in production
# .env.development
VITE_APP_TITLE=My App in development

vite在打包的时候,会自动根据开发环境注入不同的环境变量,我们可以读取这些环境变量并在需要的地方进行使用,如vite配置文件,src源代码中等等

// 使用
console.log(import.meta.env.VITE_APP_TITLE)

总结

到此这篇关于vue3实战教程之axios的封装和环境变量的文章就介绍到这了,更多相关vue3 axios封装和环境变量内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

你可能感兴趣的:(vue3实战教程之axios的封装和环境变量)