TS 完全指南之实战:封装 axios 和 带过期时间的 localStorage(七)

1,202 阅读3分钟

实战:axios

  • 支持请求和响应拦截器
  • 支持取消请求和取消全部请求的功能
  • 提供了 GET、POST、PUT、DELETE 四种请求方法
import axios from "axios"
import type { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosRequestConfig } from "axios"

class Request {
  // Axios 实例
  instance: AxiosInstance
  // 存放取消请求控制器
  abortControllerMap: Map<string, AbortController>

  constructor(config: AxiosRequestConfig) {
    // 创建 Axios 实例
    this.instance = axios.create(config)
    // 存储取消请求的控制器
    this.abortControllerMap = new Map()
    // 添加请求拦截器
    this.instance.interceptors.request.use(
      (res: InternalAxiosRequestConfig) => {
        // 创建取消请求的控制器
        const controller = new AbortController()
        // 获取请求的 url
        const url = res.url || ""
        // 将控制器存储到 Map 中
        res.signal = controller.signal
        this.abortControllerMap.set(url, controller)
        return res
      },
      (err: any) => err
    )

    // 添加响应拦截器
    this.instance.interceptors.response.use(
      (res: AxiosResponse) => {
        // 获取响应的 url
        const url = res.config.url || ""
        // 从 Map 中删除对应的控制器
        this.abortControllerMap.delete(url)
        return res.data
      },
      (err: any) => err // 响应拦截器错误处理函数
    )
  }

  // 发送请求的方法,返回 Promise 对象
  request<T>(config: AxiosRequestConfig<T>): Promise<T> {
    return new Promise((resolve, reject) => {
      this.instance
        .request(config)
        .then(res => {
          resolve(res as T)
        })
        .catch((err: any) => {
          reject(err)
        })
    })
  }

  // 取消全部请求
  cancelAllRequest() {
    for (const controller of this.abortControllerMap.values()) {
      controller.abort()
    }
    // 清空 Map
    this.abortControllerMap.clear()
  }

  // 取消指定的请求
  cancelRequest(url: string | string[]) {
    // 将参数转换为数组
    const urlList = Array.isArray(url) ? url : [url]
    urlList.forEach(_url => {
      // 根据 url 获取对应的控制器并取消请求
      this.abortControllerMap.get(_url)?.abort()
      // 从 Map 中删除对应的控制器
      this.abortControllerMap.delete(_url)
    })
  }

  // 发送 GET 请求的方法,返回 Promise 对象
  async get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
    return this.request<T>({ ...config, method: "get", url })
  }

  // 发送 POST 请求的方法,返回 Promise 对象
  async post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
    return this.request<T>({ ...config, method: "post", url, data })
  }

  // 发送 PUT 请求的方法,返回 Promise 对象
  async put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
    return this.request<T>({ ...config, method: "put", url, data })
  }
  // 发送 DELETE 请求的方法,返回 Promise 对象
  async delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
    return this.request<T>({ ...config, method: "delete", url })
  }
}

const myRequest = new Request({
  baseURL: "https://www.fastmock.site/mock/13089f924ad68903046c5a61371475c4",
  timeout: 10000,
})

export default myRequest

使用:

<script setup lang="ts">
import myRequest from "./axios"
import { onMounted } from "vue"

interface Req {
  name: string
}
interface Res {
  code: string
  data: {
    userName: string
  }
}

const getData = (data: Req) => {
  return myRequest.request<Res>({
    url: "/api/user/login",
    method: "POST",
    data
  })
}

onMounted(async () => {
  const res = await getData({
    name: "云牧"
  })
  console.log(res)
})
</script>

实战:localStorage

interface LocalStorageItem<T> {
  value: T // 存储的值
  expire: number | null // 过期时间,如果为 null 则表示永不过期
}

class LocalStorage {
  private static instance: LocalStorage // 单例模式,保证只有一个实例
  private storage: Storage // localStorage 对象

  private constructor() {
    this.storage = window.localStorage // 获取 localStorage 对象
  }

  public static getInstance(): LocalStorage {
    if (!LocalStorage.instance) {
      // 如果实例不存在,则创建一个新实例
      LocalStorage.instance = new LocalStorage()
    }
    return LocalStorage.instance // 返回实例
  }

  public setItem<T>(key: string, value: T, expire?: number): void {
    const item: LocalStorageItem<T> = {
      value: value, // 存储的值
      expire: expire ? new Date().getTime() + expire : null, // 过期时间
    }
    this.storage.setItem(key, JSON.stringify(item)) // 将对象序列化为字符串并存储到 localStorage 中
  }

  public getItem<T>(key: string): T | null {
    const itemStr = this.storage.getItem(key) // 获取存储的字符串
    if (itemStr) {
      // 如果字符串存在
      const item: LocalStorageItem<T> = JSON.parse(itemStr) // 将字符串反序列化为对象
      if (!item.expire || new Date().getTime() < item.expire) {
        // 如果没有过期或者还没有过期
        return item.value // 返回存储的值
      } else {
        this.storage.removeItem(key) // 如果已经过期,则删除该项
      }
    }
    return null // 如果不存在或者已经过期,则返回 null
  }

  public removeItem(key: string): void {
    this.storage.removeItem(key) // 删除指定的项
  }

  public clear(): void {
    this.storage.clear() // 清空 localStorage
  }
}

export default LocalStorage.getInstance() // 导出 LocalStorage 实例

使用:

<script lang="ts" setup>
import storage from "./storage"

// 存储数据
storage.setItem("name", "黛玉", 60 * 60 * 1000) // 存储一个过期时间为 1 小时的数据
storage.setItem("person", { name: "云牧" }, 60 * 60 * 1000) // 存储一个过期时间为 1 小时的数据

interface Person {
  name: string
}

// 获取数据
const name = storage.getItem<string>("name")
const person = storage.getItem<Person>("person")
console.log(name) // 黛玉
console.log(person.name) // 云牧

// 删除数据
storage.removeItem("name")

// 清空所有数据
storage.clear()
</script>