Vue 프로젝트에 인터페이스 감청 마스크를 추가하는 방법

1. 업무 배경


마스크 레이어를 사용하여 사용자의 비정상적인 동작을 차단하는 것은 전단에서 자주 사용하는 방식이다.그러나 일부 항목에서 마스크 레이어를 통일적으로 관리하지 않으면 다음과 같은 문제가 발생한다.
(1) 모든 업무 구성 요소는 마스크층 구성 요소, 즉 모든 것을 도입해야 한다.vue 업무 구성 요소, 모두template에 Mask 구성 요소를 도입했습니다.구성 요소는 프로젝트의 구석구석에 존재하기 때문에 관리에 불리하고 코드가 극도로 번거롭다.
(2) Mask 구성 요소는 모두 업무의 구석구석으로 분산되어 있기 때문에 마스크층을 표시하는지 여부를 제어하는 변수도 업무 구성 요소에 흩어져 있다.예를 들어maskShow를 사용하여 마스크층을 보여줄지 여부를 제어할 때 비교적 복잡한 프로젝트에서 200+의maskShow 변수가 발생합니다.
(3)maskShow가 너무 많고 업무에 융합된 동시에maskShow의 변수는 인터페이스의 리셋 함수에 쓰여 변수를 바꾸는 것을 잊어버리는 경우가 자주 발생하여 마스크층이 표시하는 것과 표시하지 않는 논리적 오류를 초래한다.
(4) 프로젝트는 로컬 디버깅을 자주 하지만 실제 실행은 온라인입니다. (3) 문제는 로컬에서 검증할 수 없습니다.이런 문제들은 온라인 네트워크 환경이 비교적 좋지 않은 경우에 자주 발생하기 때문이다.만약에 단추를 눌렀을 때 인터페이스가 되돌아와야 다시 클릭할 수 있지만 로컬은 되돌아오는 속도가 빨라서 마스크층을 추가하는 것을 잊어버리면 아무런 문제가 없습니다.그러나 네트워크에 문제가 있는 온라인 환경이라면 쉽게 나타날 수 있고 이 문제가 발생하면 위치를 정하기 어려워 업무 효율에 큰 영향을 미친다.

2. 문제 분석


상술한 배경에 따라 실제 프로젝트에 공공 마스크층 구성 요소를 추가하여 관리하면 매우 의미가 있다.분석을 통해 구체적으로 다음과 같은 문제를 해결해야 한다.
(1) 마스크 레이어가 나타나고 닫힐 시기입니다.
(2) Mask 어셈블리 설계.
(3) 이 구성 요소가 어떻게 우아하게 프로젝트에 도입되어 결합되지 않는가.
(4) 기존 프로젝트에서 기존의maskShow 방식을 점진적으로 교체하여 대면적 문제를 일으키지 않도록 하는 방법.
(5) 세부사항

3. 구성 요소 설계


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 상태의 인터페이스가 없으면 이 연결된 키를 삭제합니다.객체가 비어 있고 마스크 레이어가 있는 경우 마스크 레이어를 삭제합니다.

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?: 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) 필터 인터페이스의'?',그리고hash모드의'#',

//  url
getMonitorUrl(url: string): string{
 const urlIndex: number = url.indexOf(`?`);
 let monitorUrl: string = url;
 if (urlIndex !== -1) {
 monitorUrl = url.substring(0, urlIndex);
 }
 return monitorUrl;
}
//  path
getMonitorPath(): string{

 const path: string = this.mode === HASH_TYPE ? 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(`?`);

 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()


4. 총결산


본고는 통일된 커버층의 배경, 문제점과 디자인 방안을 소개했다.그러나 모든 세부 사항을 열거하지 않았으니 실제 업무에 따라 선택해야 한다.그러나 대체 시나리오는 다음과 같습니다.
(1) 마스크층은 일부 인터페이스pending이 설치될 때 표시되고 모든 인터페이스가 되돌아오면 자동으로 닫힙니다.여기의 인터페이스는 감청이 필요한 인터페이스를 가리킨다
(2) 구성 요소의 가장 중요한 두 가지 함수는 appendMask에 마스크 층을 추가하고removeMask에서 마스크 층을 삭제합니다.
(3) 마스크가 완전히 독립되고 제3자 라이브러리(axios)의 리셋에 의존하지 않으려면 XMLHttpRequest를 직접 개작할 수 있지만 이렇게 하는 것은 위험이 매우 크기 때문에 건의하지 않는다.
(4) 구성 요소는 통일된 팀원이 스스로 루트와 감청 인터페이스를 설정하는 방식을 교체한다.이곳의 논리는 감청할 인터페이스가 많으면 블랙리스트를 사용할 수 있고, 반대로 화이트리스트를 사용할 수 있다.
(5) 렌더링 최적화, 파라미터 요청, 루트 모드 최적화.
Vue 프로젝트에 인터페이스 감청 마스크를 추가하는 방법에 대한 이 글은 여기에 소개되었습니다. 더 많은 Vue 인터페이스 감청 마스크 내용은 저희 이전의 글을 검색하거나 아래의 관련 글을 계속 훑어보십시오. 앞으로 많은 응원 부탁드립니다!

좋은 웹페이지 즐겨찾기