热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

如何在Vue项目中添加接口监听遮罩

这篇文章主要介绍了如何在Vue项目中添加接口监听遮罩,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

一、业务背景

使用遮罩层来屏蔽用户的非正常操作,是前端经常使用的方式。但是在一些项目中,并没有对遮罩层进行统一管理,这就会造成如下的问题:
(1)所有的业务组件都要引入遮罩层组件,也就是每个.vue业务组件,都在template中引入了Mask组件。组件在项目的各个角落都存在,不利于管理,代码极度冗余。
(2)Mask组件都分散到业务的各个角落,所以控制是否显示遮罩层的变量也散在业务组件中。比如使用maskShow来控制是否展示遮罩层时,一个较为复杂的项目中会产生200+的maskShow变量。
(3)maskShow过多且融入在业务中,同时maskShow的变量往往写在接口的回调函数中,经常会出现忘记改变变量的情况,造成遮罩层该显示和不该显示的逻辑出错。
(4)项目经常是在本地调试,而真实运行却又在线上,(3)中的问题在本地经常无法验证出。因为这些问题经常是在线上网络环境较差的情况出现。如一个按钮按完之后,需要等接口返回才能再次点击,但是本地因为返回速度较快,如果忘记添加遮罩层也不会有什么问题。但如果是网络有问题的线上环境,就很容易出现,且该问题一旦出现,很难定位,大大影响工作效率。

二、问题分析

根据上述的背景,在实际项目中添加一个公共的遮罩层组件进行管理,就变的十分有意义。经过分析,具体需要解决如下问题:
(1)遮罩层出现和关闭的时机。
(2)Mask组件设计。
(3)该组件如何优雅的引入到项目中,不产生耦合。
(4)如何在已有的项目中,渐进式的更换原有的maskShow的方式,从而不造成大面积问题。
(5)细节问题

三、组件设计

1、遮罩层出现和关闭的时机

该问题根据不同业务需求决定,但是笔者认为,大部分遮罩的出现和关闭主要取决于接口的请求和返回,一个接口在请求pending状态下,显示遮罩层,所有接口返回则关闭遮罩。本文主要解决的是接口请求遮罩问题,使用ts进行编写,且并不会罗列所有细节。

2、Mask组件设计

Mask组件为一个class,将细节屏蔽在class内部。
(1)class内部最主要功能为添加和删除遮罩层,传输的当前请求接口的url。

class Mask {
 // 显示遮罩层
 appendMask(url: string): void{}

 // 删除遮罩层
 removeMaskl(url: string): void{}
}

(2)添加遮罩层函数,请求时调用该函数,传入当前接口url。函数内部维护一个监听对象,用以监听当前是否存在pending状态的请求。该对象的value为该接口pending状态的数量。通过假设遮罩视图组件已经挂载到了Vue原型链上,如果没有,则在组件上方引入即可。

// 监听对象数据类型定义
interface HTTPDictInterface {
 [index: string]: number;
}

appendMask(url: string): void{ 

 if(!this.monitorHTTPDict[url]){
 this.monitorHTTPDict[url] = 0;
 }
 this.monitorHTTPDict[url] += 1;

 // 如果存在监听接口,则显示遮罩层
 if(!this.mask && Object.keys(this.monitorHTTPDict).length){

 // 在body上添加遮罩层样式,$Mask为遮罩层样式组件
 const COnstructor= Vue.extend(Vue.prototype.$Mask);
 this.mask = new Constructor().$mount();

 document.body.appendChild(this.mask.$el);
 }
}

(3)删除遮罩层函数,每次请求结束之后都会调用该函数,当发现请求监听对象为空时,删除的遮罩层。如果没有pending状态的接口,删除该对接的key。该对象为空且有遮罩层的情况下,删除遮罩层。

removeMask(url: string): void{

 // 成功返回后
 if (this.monitorHTTPDict[monitorUrl]) {
 this.monitorHTTPDict[monitorUrl] -= 1;
 if (this.monitorHTTPDict[monitorUrl] <= 0) {
 delete this.monitorHTTPDict[monitorUrl];
 }
 }

 // hasMask用以检测页面是否存在遮罩层标签元素
 if (this.mask && this.hasMask() && !Object.keys(this.monitorHTTPDict).length) {
 document.body.removeChild(this.mask.$el);
 this.mask = null;
 }

 this.timer = null;
}

3、该组件如何优雅的引入到项目中,不产生耦合。

使用该组件,需要在所有的请求发起之前调用appendMask函数,所有的请求结束之后调用removeMask函数。这就有如下两种调用方式。
(1)使用axios等组件的回调,完成函数调用。但是这种做法并没有将Mask组件的代码独立于项目,它依赖于具体接口框架的API。

instance.interceptors.request.use((config) => {

 // 添加遮罩层
 mask.appendMask(config.url);

 return config;
});

(2)添加init函数,直接在原生XMLHttpRequest对象中注入回调。更改原生XMLHttpRequest函数,在事件'loadstart'和'loadend'中注入回调,需要注意的是,loadstart接收的传参中,并没有当前请求的url,所以还需要改写open函数,把open接收传参的url挂载到新的xhr对象上。慎用该方法。因为更改原生API的方式十分危险,在很多编码规范中是禁止的,如果所有人都对原生API进行改写,当同时引入这些框架会产生冲突,造成无法意料的后果。

// 通过传参来决定是否使用该方法

init(){
 if (this.autoMonitoring){
 this.initRequestMonitor();
 }
}

// 新的xmlhttprequest类型
interface NewXhrInterface extends XMLHttpRequest{
 requestUrl&#63;: string
}

// 原生注入
initRequestMonitor(): void{

 let OldXHR = window.XMLHttpRequest;
 let maskClass: Mask = this;

 // @ts-ignore,编码规范不允许修改XMLHttpRequest
 window.XMLHttpRequest = function () {

 let realXHR: NewXhrInterface = new OldXHR();
 let oldOpen: Function = realXHR.open;

 realXHR.open = (...args: (string | boolean | undefined | null)[]): void => {

 realXHR.requestUrl = (args[1] as string);
 oldOpen.apply(realXHR, args);

 };

 realXHR.addEventListener(`loadstart`, () => {

 const requestUrl: string = (realXHR.requestUrl as string);

 const url: string = maskClass.cleanBaseUrl(requestUrl);

 // 开启遮罩
 maskClass.appendMask(url);
 });

 realXHR.addEventListener(`loadend`, () => {

 const responseURL: string = (realXHR as XMLHttpRequest).responseURL;
 const url: string = maskClass.cleanBaseUrl(responseURL);

 // 删除遮罩
 maskClass.removeMask(url);
 });

 return realXHR;
 };
}

(3)注入使用方式,直接调用init。这样改项目的所有请求都会经过Mask。

new Mask().init()

4、如何在已有的项目中,渐进式的更换原有的maskShow的方式,从而不造成大面积问题。

如果直接在全项目中使用,牵扯的面积就会变得很广,会大面积的产生问题,反而得不偿失。所以应该采取一种渐进更换的方式,做到平滑过渡。主要思路是通过配置页面和黑名单的方式,来决定哪些页面引入该组件,从而让每个组员自己修改,毕竟页面的负责人才是最了解当前页面业务的人。至于如何黑名单还是白名单,则由项目的具体业务决定。

// key需要监听的路由页面,value为一个数组,数组中填写的接口为黑名单,不需要监听的接口
const PAGE_OnE= `/home`;
const PAGE_TWO = `/login`;
const HTTO_OnE= `xxx`

export const maskUrlList = {
 [PAGE_ONE]: [HTTO_ONE],
 [PAGE_TWO]: [],
};

appendMask方法过滤黑名单和没有配置的页面。maskUrlList为控制的对象,先检查页面路由,之后检查是否存在黑名单。

appendMask(url: string): void{

 // 获取当前页面的path,获取页面路径,根据hash和history模式进行区分
 const monitorPath: string = this.getMonitorPath();

 // maskUrlList为配置项,先检查页面路由,之后检查是否存在黑名单
 if (this.maskUrlList[monitorPath]
 && !this.maskUrlList[monitorPath].includes(url)) {
 if (this.monitorHTTPDict[url] === undefined) {
 this.monitorHTTPDict[url] = 0;
 }
 this.monitorHTTPDict[monitorUrl] += 1;
 }

 // 添加遮罩层
 if (!this.mask && this.hasMonitorUrl()) {
 const COnstructor= Vue.extend(Vue.prototype.$Mask);
 this.mask = new Constructor().$mount();

 document.body.appendChild(this.mask.$el);
 }
}

5、细节问题

(1)渲染之后才关闭遮罩层,将实际删除遮罩层逻辑放到定时器中,Vue的异步渲染采用的promise,所以关闭在如果放在渲染之后,需要放入setTimeout中。这里涉及到事件循环的知识。当接口返回,如果需要渲染页面,则会异步执行一个Promise,Promise为微任务,setTimeout为宏任务,当主线程执行完毕后,会先执行微任务,之后才会执行异步的宏任务setTimeout。

// 清理遮罩层
if (!this.timer) {
 this.timer = window.setTimeout(() => {

 if (this.mask && this.hasMask() && !this.hasMonitorUrl()) {
 document.body.removeChild(this.mask.$el);
 this.mask = null;
 }

 this.timer = null;

 }, 0);
}

(2)过滤接口的‘&#63;',以及hash模式下的‘#',

// 获取请求接口的url
getMonitorUrl(url: string): string{
 const urlIndex: number = url.indexOf(`&#63;`);
 let monitorUrl: string = url;
 if (urlIndex !== -1) {
 mOnitorUrl= url.substring(0, urlIndex);
 }
 return monitorUrl;
}
// 获取当前路由path
getMonitorPath(): string{

 const path: string = this.mode === HASH_TYPE &#63; window.location.hash : window.location.pathname;

 let monitorPath: string = path;

 if (this.mode === HASH_TYPE) {
 mOnitorPath= monitorPath.substring(path.indexOf(`#`) + 1);
 }

 // 截图路径,删除请求参数
 const hashIndex: number = monitorPath.indexOf(`&#63;`);

 if (hashIndex !== -1) {
 mOnitorPath= monitorPath.substring(0, hashIndex);
 }

 return monitorPath;
}

(3)接口过滤baseUrl。细心的话,会发现在使用axios的接口时,自行决定是否带入baseUrl,那是因为axios会在请求时进行区分过滤。如果项目前期并没有很好的定义使用方式的话,会有两种不同使用axios的方式。那么,就需要对baseUrl进行过滤。

// 去除baseUrl
cleanBaseUrl(fullUrl: string): string {

 const baseUrlLength: number = this.baseUrl.length;
 return fullUrl.substring(baseUrlLength);
 
}

(4)组件初始化,通过传入params的方式,将对象实例化出来。

new Mask({
 modeType, // hash或history
 autoMonitoring, // 是否更写原生XMLHttpRequest对象
 maskUrlList, // 配置引入的页面和接口
 baseUrl, // 当前项目的baseUrl
 ...
}).init()

四、总结

本文介绍了统一遮罩层的背景、问题及设计方案。但并没有将所有细节进行列举,这需要根据实际业务进行选择。但大体方案已经列出:
(1)遮罩层应该在一些接口pending装的时候显示,所有接口返回后自动关闭。这里的接口是指需要监听的接口
(2)组件最重要的两个函数为appendMask添加遮罩层和removeMask删除遮罩层。
(3)如果想Mask完全独立,并不想依赖于第三方库(axios)的回调,可以直接对XMLHttpRequest进行改写,但这样做风险很大,并不建议。
(4)组件更换统一组员自己配置路由及监听接口的方式。这里的逻辑可以自行决定,如果要监听的接口多,可以采用黑名单,反之则白名单。
(5)对渲染的优化、请求带参数、路由的模式进行了优化。

到此这篇关于如何在Vue项目中添加接口监听遮罩的文章就介绍到这了,更多相关Vue 接口监听遮罩内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • r2dbc配置多数据源
    R2dbc配置多数据源问题根据官网配置r2dbc连接mysql多数据源所遇到的问题pom配置可以参考官网,不过我这样配置会报错我并没有这样配置将以下内容添加到pom.xml文件d ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 如何在服务器主机上实现文件共享的方法和工具
    本文介绍了在服务器主机上实现文件共享的方法和工具,包括Linux主机和Windows主机的文件传输方式,Web运维和FTP/SFTP客户端运维两种方式,以及使用WinSCP工具将文件上传至Linux云服务器的操作方法。此外,还介绍了在迁移过程中需要安装迁移Agent并输入目的端服务器所在华为云的AK/SK,以及主机迁移服务会收集的源端服务器信息。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 本文介绍了在使用MSXML解析XML文件时出现DTD禁用问题的解决方案。通过代码示例和错误信息获取方法,解释了默认情况下DTD是禁用的,以及如何启用DTD的方法。此外,还提到了网上关于该问题的信息相对较少,因此本文提供了解决方案以供参考。 ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 本文是关于自学Android的笔记,包括查看类的源码的方法,活动注册的必要性以及布局练习的重要性。通过学习本文,读者可以了解到在自学Android过程中的一些关键点和注意事项。 ... [详细]
  • 本文介绍了使用cacti监控mssql 2005运行资源情况的操作步骤,包括安装必要的工具和驱动,测试mssql的连接,配置监控脚本等。通过php连接mssql来获取SQL 2005性能计算器的值,实现对mssql的监控。详细的操作步骤和代码请参考附件。 ... [详细]
  • 本文讨论了如何在codeigniter中识别来自angularjs的请求,并提供了两种方法的代码示例。作者尝试了$this->input->is_ajax_request()和自定义函数is_ajax(),但都没有成功。最后,作者展示了一个ajax请求的示例代码。 ... [详细]
  • 突破MIUI14限制,自定义胶囊图标、大图标样式,支持任意APP
    本文介绍了如何突破MIUI14的限制,实现自定义胶囊图标和大图标样式,并支持任意APP。需要一定的动手能力和主题设计师账号权限或者会主题pojie。详细步骤包括应用包名获取、素材制作和封包获取等。 ... [详细]
  • 延迟注入工具(python)的SQL脚本
    本文介绍了一个延迟注入工具(python)的SQL脚本,包括使用urllib2、time、socket、threading、requests等模块实现延迟注入的方法。该工具可以通过构造特定的URL来进行注入测试,并通过延迟时间来判断注入是否成功。 ... [详细]
author-avatar
忧之灵_435
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有