1.1.2 • Published 11 months ago

st-duplicate-request-axios v1.1.2

Weekly downloads
-
License
ISC
Repository
-
Last release
11 months ago

介绍:

在现代的前端开发中,与后端进行数据交互是一个非常常见的任务。然而,我们经常会遇到一个令人头疼的问题:请求重复提交。当用户在某个操作上频繁点击或者网络延迟导致请求未完成时,重复提交请求会带来一系列问题,如重复创建数据、产生不一致的状态等,给用户体验和数据完整性带来困扰。

为了解决这个问题,我封装了一款的工具:St-Duplicate-Request-Axios。这个工具基于 Vue 3 和 Axios,并提供了一个自定义 Hook,帮助开发者轻松处理请求重复提交的场景,从而提升开发效率。

使用方法

  1. 首先,安装依赖包。在终端中运行以下命令:
npm i st-duplicate-request-axios
  1. 在需要使用的文件中引入 useStDuplicateRequestAxios 函数,并创建一个实例:
import axios from "axios"
import { useStDuplicateRequestAxios } from "st-duplicate-request-axios"
const duplicateRequest = useStDuplicateRequestAxios(axios)
  1. 现在,你可以在发送请求之前使用 duplicateRequest.addRequest 函数将请求添加到请求队列中。如果队列中已存在相同的请求,则会取消重复的请求:
// 请求拦截
serve.interceptors.request.use((config: InternalAxiosRequestConfig) => {
    duplicateRequest.addRequest(config) // 添加进请求队列
    ...
    return config
}, (error: AxiosError) => {
    return Promise.reject(error)
})
  1. 当请求完成时,使用 duplicateRequest.removeRequest 函数将请求从队列中移除:
// 响应拦截
serve.interceptors.response.use((response: AxiosResponse) => {
    duplicateRequest.removeRequest(response.config) // 移除请求队列
   ...
    return response
}, (error: AxiosError) => {
    message.warning(error.message)
    return Promise.reject(error)
})
  1. 你也可以传入两个可选的回调函数:重复请求回调函数和移除请求回调函数。
// 重复请求回调函数
const handleDuplicateRequest = (cancel, config, requestQueue) => {
  console.log("发现重复请求,取消当前请求:", config.url)
  cancel("请求重复,已取消") // 可自定义取消请求的错误信息
}

// 移除请求回调函数
const handleRemoveRequest = (config, requestQueue) => {
  console.log("请求完成,从队列中移除:", config.url)
}

// 请求拦截器
axios.interceptors.request.use(
  (config) => {
    duplicateRequest.addRequest(config, handleDuplicateRequest) // 添加进请求队列并传入重复请求回调函数
    // 其他逻辑...
    return config
  },
  (error: AxiosError) => {
    return Promise.reject(error)
  }
)

// 响应拦截器
axios.interceptors.response.use(
  (response) => {
    duplicateRequest.removeRequest(response.config, handleRemoveRequest) // 移除请求队列并传入移除请求回调函数
    // 其他逻辑...
    return response
  },
  (error: AxiosError) => {
    // 错误处理逻辑...
    return Promise.reject(error)
  }
)

代码实现:

  1. 导入必要的依赖和类型声明:

    import { AxiosStatic, InternalAxiosRequestConfig, Canceler } from "axios"
    import { reactive } from "vue"
    import { isEqual } from "lodash-es"

    这段代码导入了 Axios 相关的类型声明和必要的依赖,包括 AxiosStatic、InternalAxiosRequestConfig 和 Canceler,以及 reactive 方法用于创建响应式对象,和 isEqual 方法用于比较数据的相等性。

  2. 创建请求队列和数据归一化函数:

    const requestQueue = reactive<InternalAxiosRequestConfig[]>([])
    
    type DataType = object | string
    
    const _formatData = (data: DataType) => {
        if (typeof data === "string") {
            try {
                return JSON.parse(data)
            } catch (error) {
                return {}
            }
        } else if (typeof data === "object") {
            return data
        } else {
            return {}
        }
    }

    这段代码创建了一个响应式的请求队列 requestQueue,用于存储待发送的请求配置。_formatData 函数用于将数据归一化,将数据统一转换为对象形式。

  3. 获取请求队列中存在的索引:

    typescriptCopy code
    const _getArrayIndex = (config: InternalAxiosRequestConfig) => {
        if (!requestQueue.length) return -1;
        else return requestQueue.findIndex((queueConfig: InternalAxiosRequestConfig) => {
            return (queueConfig.url === config.url && isEqual(_formatData(config.data), _formatData(queueConfig.data)))
        })
    }

    这段代码实现了一个函数 _getArrayIndex,用于获取请求队列中与指定请求配置相同的请求的索引。它会遍历请求队列中的配置,判断 URL 和数据是否相同,如果找到相同的请求,则返回其索引,否则返回 -1。

  4. 添加请求到队列中:

    const addRequest = (config: InternalAxiosRequestConfig, repeatCb?: repeatCbType) => {
        if (!requestQueue.length || _getArrayIndex(config) === -1) {
            requestQueue.push(config)
        } else {
            config.cancelToken = new axios.CancelToken((cancel: Canceler) => {
                if (repeatCb) repeatCb(cancel, config, requestQueue);
                else cancel(`请求重复,请求地址:${config.url}`)
            })
        }
    }

    这段代码定义了 addRequest 函数,用于将请求配置添加到请求队列中。如果请求队列为空或者不存在相同的请求,则将请求配置添加到队列中;否则,会使用 axios 提供的 CancelToken 取消重复的请求,并执行 repeatCb 回调函数(如果提供的话)。

  5. 从队列中移除完成的请求:

    const removeRequest = (config: InternalAxiosRequestConfig, removedSuccessCb?: removedSuccessCbType) => {
        if (requestQueue.length) {
            const index = _getArrayIndex(config)
            if (index !== -1) requestQueue.splice(index, 1)
            removedSuccessCb && removedSuccessCb(config, requestQueue)
        }
    }

    这段代码实现了 removeRequest 函数,用于从请求队列中移除已完成的请求配置。它会找到与指定请求配置相同的请求,并将其从队列中移除。移除成功后,会执行 removedSuccessCb 回调函数(如果提供的话)。

    addRequestremoveRequest 函数是我们保留出来的两个函数,用于处理请求的重复提交。

import { AxiosStatic, InternalAxiosRequestConfig, Canceler } from "axios"
import { reactive } from "vue"
import { isEqual } from "lodash-es"

// st-no-duplicate-request-axios
export function useStDuplicateRequestAxios(axios: AxiosStatic) {
    const requestQueue = reactive<InternalAxiosRequestConfig[]>([])

    type DataType = object | string
    /**
     * 数据归一化
     * @param data 
     * @returns 
     */
    const _formatData = (data: DataType) => {
        if (typeof data === "string") {
            try {
                return JSON.parse(data)
            } catch (error) {
                return {}
            }
        } else if (typeof data === "object") {
            return data
        } else {
            return {}
        }
    }


    /**
     * 获取请求队列中存在的索引
     * @param config 
     * @returns 
     */
    const _getArrayIndex = (config: InternalAxiosRequestConfig) => {
        if (!requestQueue.length) return -1;
        else return requestQueue.findIndex((queueConfig: InternalAxiosRequestConfig) => {
            return (queueConfig.url === config.url && isEqual(_formatData(config.data), _formatData(queueConfig.data)))
        })
    }


    /**
     * 如果请求队列中没有一样的请求,就加入到请求队列中,否则则取消请求
     * @param config 
     * @param repeatCb Function 
     */
    const addRequest = (config: InternalAxiosRequestConfig, repeatCb?: repeatCbType) => {
        if (!requestQueue.length || _getArrayIndex(config) === -1) {
            requestQueue.push(config)
        } else {
            config.cancelToken = new axios.CancelToken((cancel: Canceler) => {
                if (repeatCb) repeatCb(cancel, config, requestQueue);
                else cancel(`请求重复,请求地址:${config.url}`)
            })
        }
    }


    /**
     * 请求完成,把请求从队列中移除
     * @param config 
     * @param removedSuccessCb 移除成功的回调函数
     */
    const removeRequest = (config: InternalAxiosRequestConfig, removedSuccessCb?: removedSuccessCbType) => {
        if (requestQueue.length) {
            const index = _getArrayIndex(config)
            if (index !== -1) requestQueue.splice(index, 1)
            removedSuccessCb && removedSuccessCb(config, requestQueue)
        }
    }


    return {
        addRequest,
        removeRequest
    }
}

使用上述代码,我们可以在 Vue 3 项目中轻松地处理请求的重复提交问题。addRequest 函数用于将请求添加到请求队列中,如果队列中已存在相同的请求,则会取消重复的请求;removeRequest 函数用于从请求队列中移除已完成的请求。

这个自定义的 Hook 可以帮助我们优化项目中的请求处理,避免重复提交请求,提高系统的性能和用户体验。在使用时,我们只需引入该 Hook,并在需要的地方调用 addRequestremoveRequest 函数即可。

特点

  1. 简单易用:只需要引入 St-Duplicate-Request-Axios,然后使用自定义的 Hook,就可以快速集成到你的项目中。
  2. 自动处理重复请求:无需手动编写逻辑判断重复请求,工具会自动根据请求的 URL 和数据判断是否重复,并进行处理。
  3. 数据归一化:通过归一化请求数据,保证数据内容相同的请求也能被正确判断为重复请求。
  4. 灵活定制:可以自定义重复请求的处理逻辑,例如弹出提示、取消请求等,以适应不同的业务场景。

结语:

useStDuplicateRequestAxios 自定义 Hook 的出现,为我们解决了请求重复提交的痛点。通过简洁而强大的代码,我们能够轻松应对各种复杂的请求场景,提高代码的可读性和维护性。无论是大型项目还是小型应用,使用 useStDuplicateRequestAxios 都能够极大地减少开发工作量,加快开发进度。

让我们一起解放双手,优化前端开发!

1.1.2

11 months ago

1.1.1

11 months ago

1.1.0

11 months ago

1.0.1

11 months ago

1.0.0

11 months ago