Vue.js中如何在子组件发起Axios请求时自动触发<b-overlay>遮罩
自动联动b-overlay与子组件Axios请求的实现方案
该方案基于Vue的provide/inject依赖注入能力,无需逐层透传props,不需要引入全局状态管理,完全实现作用域隔离,不会影响页面其他组件的请求逻辑
基础实现(轻量场景优先)
父组件(b-overlay所在组件)
核心逻辑是本地维护请求计数器,通过provide把绑定计数器的请求方法跨层级注入给所有子组件,b-overlay的显示状态直接绑定计数器是否大于0:
<template> <b-overlay :show="loadingCount > 0" rounded> <child> <other-child /> </child> </b-overlay> </template> <script setup> import { ref, provide } from 'vue' import axios from 'axios' // 本地请求计数器,处理并发请求:所有请求完成才关闭遮罩 const loadingCount = ref(0) // 封装与当前遮罩绑定的请求函数,注入给内部所有子组件 provide('scopedAxiosRequest', async (config) => { loadingCount.value += 1 try { // 独立Axios实例,避免与全局配置冲突 const requestInstance = axios.create() return await requestInstance(config) } finally { // 无论请求成功/失败都要重置计数,避免遮罩卡死 loadingCount.value -= 1 } }) </script>
任意层级子组件的使用方式
不管嵌套多少层,子组件直接inject注入的请求方法发请求即可,完全不需要感知遮罩的存在,也不需要手动控制遮罩状态:
<script setup> import { inject } from 'vue' // 跨层级接收父组件提供的作用域请求方法 const scopedRequest = inject('scopedAxiosRequest') const loadTableData = async () => { // 直接调用即可,自动触发遮罩显示/隐藏 const res = await scopedRequest({ url: '/api/table/list', method: 'get' }) // 正常处理返回数据即可 } </script>
进阶实现(适配已封装全局Axios的项目)
如果项目已经统一封装了全局Axios实例,不需要单独创建独立实例,可以通过唯一作用域标识的方式实现适配:
- 维护全局的作用域计数器映射表,存储每个遮罩对应的请求计数器
- 父组件生成唯一的作用域标识,把标识和计数器存入映射表,注入给子组件
- 子组件发起请求时自动把标识附加到全局Axios的请求配置中
- 全局Axios拦截器根据请求携带的标识,自动操作对应遮罩的计数器
核心代码示例:
// 全局映射表:存每个遮罩作用域的计数器 const overlayCounterMap = new Map() // 父组件逻辑 const overlayScopeId = Symbol('overlay-scope') // 唯一标识,不能复用 const loadingCount = ref(0) overlayCounterMap.set(overlayScopeId, loadingCount) // 注入给子组件的请求方法 provide('scopedRequest', (config) => { return axios({ ...config, overlayScope: overlayScopeId // 把标识附加到请求配置 }) }) // 全局Axios请求拦截器 axios.interceptors.request.use((config) => { if (config.overlayScope) { const counter = overlayCounterMap.get(config.overlayScope) if (counter) counter.value += 1 } return config }) // 全局Axios响应拦截器(成功+失败都要重置计数) axios.interceptors.response.use( (res) => { if (res.config.overlayScope) { const counter = overlayCounterMap.get(res.config.overlayScope) if (counter) counter.value -= 1 } return res }, (err) => { if (err.config?.overlayScope) { const counter = overlayCounterMap.get(err.config.overlayScope) if (counter) counter.value -= 1 } return Promise.reject(err) } )
注意事项
- 计数器必须在请求失败分支/
finally中重置,否则会因为请求异常导致遮罩一直显示 - 作用域标识必须保证唯一,推荐用
Symbol()生成,避免不同遮罩的计数器互相干扰 - 当前b-overlay范围内的所有请求,必须使用注入的作用域请求方法,直接调用全局Axios不会触发联动
- 如果内部嵌套了其他
b-overlay组件,会自动遵循Vue的就近inject规则,不需要额外处理作用域覆盖逻辑
内容的提问来源于stack exchange,提问作者Vinicius Romao
相关产品推荐
相关产品推荐

