32
头图
The ideas in this article come from the refactoring summary of the actual project. Corrections and exchanges are welcome. If it is helpful to you, please also like it 👍Favorite to support it.

I recently refactored an old project and found that the request interceptor was written quite messy, so I refactored the request processing layer of the entire project, and it is now running normally in the project.

This article will share with you my thinking remodeling ideas and subsequent optimization, to facilitate share with you, I used Vue3 achieve a simple demo, the idea is the same, interested friends can in my Github View , this article will This demo implemented by Vue is introduced as an example.

In this article, I will mainly share the following points with you:

  1. Problem analysis and program design;
  2. Effect after reconstruction;
  3. development process;
  4. Late optimization point;

If you still don’t know what an HTTP request and response interceptor is, you can take a look at "77.9K Star’s Axios Project What are Worth Learning Places" .

1. Demand thinking and plan design

1. Problem analysis

At present, the old project has been developed by many colleagues, and the interceptor has the following problems:

  • The code is messy and poorly readable;
  • The responsibilities of each interceptor are chaotic and interdependent;
  • There is a logical problem;
  • Different projects within the team cannot be reused;

2. Scheme design

After analyzing the above problems, my preliminary plan is as follows:
Reference plug-in architecture design , independent of each interceptor , each interceptor pulled out into a separate file maintenance, do duty single , then interceptor scheduler scheduling and registration.

The interceptor scheduling process is as follows:
拦截器调度过程

Second, the effect after reconstruction

The code is actually relatively simple, let's take a look at the final implementation effect:

1. The directory hierarchy is clearer

After refactoring, the directory hierarchy of the request processing layer is clearer, roughly as follows:

目录分层

2. Interceptor development is more convenient

In the subsequent business development of a new interceptor, it only takes 3 steps to complete the development and use of the interceptor. The interceptor scheduler will automatically call all interceptors :

拦截器开发更加方便

3. Each interceptor has a single responsibility and is pluggable

Extract each interceptor into a file to achieve, so that each interceptor separate responsibilities and a single . When there is no need to use a certain interceptor, it can be replaced at any time, and it can be plugged and unplugged flexibly.

Three, the development process

I am here to be alone out of the this demo project example to introduce.

1. Initialize the directory structure

According to the previously designed scheme, you first need to create a directory structure in the project:

- request
    - index.js      // 拦截器调度器
  - interceptors  
    - request     // 用来存放每个请求拦截器
        - index.js  // 管理所有请求拦截器,并做排序
    - response    // 用来存放每个响应拦截器
        - index.js  // 管理所有响应拦截器,并做排序

2. Define the interceptor scheduler

Because the project uses axios request library , we need to know how to use the axios interceptor. Here is a brief look at how to use the interceptor in the

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 业务 逻辑
    return config;
  }, function (error) {
    // 业务 逻辑
    return Promise.reject(error);
  });

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 业务 逻辑
    return response;
  }, function (error) {
    // 业务逻辑
    return Promise.reject(error);
  });

From the above code, we can know that when using the interceptor, we only need to call the axios.interceptors object, so we can extract this piece of logic:

// src/request/interceptors/index.js
import { log } from '../log';
import request from './request/index';
import response from './response/index';

export const runInterceptors = instance => {
    log('[runInterceptors]', instance);
      if(!instance) return;

    // 设置请求拦截器
    for (const key in request) {
        instance.interceptors.request
            .use(config => request[key](config));
    }

    // 设置响应拦截器
    for (const key in response) {
        instance.interceptors.response
            .use(result => response[key](result));
    }

    return instance;
}

This is our core interceptor scheduler . Currently, after importing all request interceptors and response interceptors, it passes through the for loop, registers all interceptors, and finally returns the entire axios instance.

3. Define a simple request interceptor and response interceptor

Here we do a simple demonstration and create the following two interceptors:

  1. Request interceptor: setLoading , the role is to display a global Toast box before initiating the request, prompting the "loading..." copy.
  2. Response interceptor: setLoading , the role is to close the Toast box in the page after the request response.

In order to unify the development specifications, we agree that the plug-in development specifications are as follows:

/*
  拦截器名称:xxx
*/
const interceptorName = options => {
  log("[interceptor.request]interceptorName:", options);
    // 拦截器业务
  return options;
};

export default interceptorName;

First create the file src/request/interceptors/request/ under the directory setLoading.js , according to the above agreed plug-in development specifications, we complete the following plug-in development:

// src/request/interceptors/request/setLoading.js

import { Toast } from 'vant';
import { log } from "../../log";

/*
  拦截器名称:全局设置请求的 loading 动画
*/
const setLoading = options => {
  log("[interceptor.request]setLoading:", options);

  Toast.loading({
    duration: 0,
    message: '加载中...',
    forbidClick: true,
  });
  return options;
};

export default setLoading;

Then export the request interceptor, and export a array to facilitate the unified registration of the interceptor scheduler:

// src/request/interceptors/request/index.js

import setLoading from './setLoading';

export default [
    setLoading
];

In the same way, we develop a response interceptor:

// src/request/interceptors/response/setLoading.js

import { Toast } from 'vant';
import { log } from "../../log";

/*
  拦截器名称:关闭全局请求的 loading 动画
*/
const setLoading = result => {
  log("[interceptor.response]setLoading:", result);

  // example: 请求返回成功时,关闭所有 toast 框
  if(result && result.success){
    Toast.clear();
  }
  return result;
};

export default setLoading;

Export response interceptor:

// src/request/interceptors/response/index.js

import setLoading from './setLoading';
export default [
    setLoading
];

4. Set the axios interceptor globally

Following the same steps as before, I wrote a few more interceptors:
Request interceptor:

Response interceptor:

As for how to achieve, we are interested can in my Github view .

Then we can re-encapsulate request and export the 060f4f1cb737d1 object for business use:

// src/request/index.js

import axios from 'axios';
import { runInterceptors } from './interceptors/index';
export const requestConfig = { timeout: 10000 };

let request = axios.create(requestConfig);
request = runInterceptors(request);

export default request;

Finished here.

Need to initiate a request in the business, you can use it like this:

<template>
  <div><button @click="send">发起请求</button></div>
</template>

<script setup>
import request from './../request/index.js';

const send = async () => {
  const result = await request({
    url: 'https://httpbin.org/headers',
    method: 'get'
  })
}
</script>

5. Test it out

The development is almost the same here. We send a request and you can see the execution process of all interceptors as follows:

日志输出

Look at the request header information:

请求头

You can see that the request interceptor we developed has taken effect.

Four, use in Taro

Since Taro has provided Taro.request method as the request method, we do not need to use axios to send the request.

The transformation based on the above code is also very simple, only two places need to be changed:

1. Modify the method of packaging request

The main thing is to replace axios with the Taro.request method, and use the addInterceptor method to import the interceptor:

// src/request/index.js

import Taro from "@tarojs/taro";
import { runInterceptors } from './interceptors/index';

Taro.addInterceptor(runInterceptors);

export const request = Taro.request;
export const requestTask = Taro.RequestTask; // 看需求,是否需要
export const addInterceptor = Taro.addInterceptor; // 看需求,是否需要

2. Modify the interceptor scheduler

Since axios and Taro.request different methods of adding interceptors, they also need to be replaced:

import request from './interceptors/request';
import response from './interceptors/response';

export const interceptor = {
    request,
    response
};

export const getInterceptor = (chain = {}) => {
  // 设置请求拦截器
  let requestParams = chain.requestParams;
  for (const key in request) {
    requestParams = request[key](requestParams);
  }

  // 设置响应拦截器
  let responseObject = chain.proceed(requestParams);
  for (const key in response) {
    responseObject = responseObject.then(res => response[key](res));
  }
  return responseObject;
};

For the specific API, see Taro.request document, but I don’t need to introduce it here.

5. Project summary and thinking

This refactoring is mainly based on the existing business, so even after the refactoring of the request layer, there are still many points that can be optimized. At present, I think that there are these, which can be regarded as one of my TODO LISTs:

1. Separate the request layer into a library

Since the company now has many projects on independent sites, considering the unified development specifications of the projects, the request layer can be considered as a private library for maintenance.
Current thinking:

  • Refer to the plug-in architecture design, and manage all interceptors lerna
  • Upgrade TypeScript to facilitate management and development;
  • Carry out engineering transformation, adding build tools, unit tests, UMD, etc.;
  • Use documentation and development documentation are perfect.

2. Support replaceable request library

In terms of extracting this separately, it is because our front-end team currently uses more request libraries and is relatively scattered, so considering the versatility, it is necessary to add support for replaceable request library methods.
Current thinking:

  • request library adaptation layer in the existing request layer to define a unified interface;
  • Built-in adaptation of several common request libraries.

3. Develop interceptor scaffolding

The purpose of this is actually very simple. Let other people in the team directly use the scaffolding tool to quickly create an interceptor according to the built-in scaffolding template, carry out subsequent development, and unify the development specifications of the interceptor to a large extent.
Current thinking:

  • Built-in two sets of interceptor templates: request interceptor and response interceptor;
  • Scaffolding development is relatively simple, and the parameters (such as language) are determined according to business needs.

4. Enhanced interceptor scheduling

The current implementation of this function is still relatively simple, we still have to consider enhancing interceptor scheduling.
Current thinking:

  • Handling the failure of the interceptor;
  • Deal with the issue of interceptor scheduling sequence;
  • Interceptor synchronous execution, asynchronous execution, concurrent execution, loop execution, etc.;
  • Pluggable interceptor scheduling;
  • Consider referring to the Tapable plug-in mechanism;

6. Summary of this article

This article summarizes a request-level interceptor scheduling scheme through a simple project reconstruction. The purpose is to achieve a single interceptors, facilitate maintenance, and to maintain and automatically, which greatly reduces the interception of the actual business of The difficulty of getting started with the development of the device.

I still have a lot to optimize in the future. As a TODO LIST of my own, if it is made completely universal, the positioning may be more inclined to the interceptor scheduling container. Only some universal interceptors are provided, and the rest is still defined by the developer, library Responsible for scheduling, but the commonly used request libraries are generally ready, so the value of doing so needs to be weighed.

Of course, it is still prioritized to develop and use as a team's internal private library, because basically the business used within the team is similar, but the project is different.


pingan8787
3.2k 声望4.1k 粉丝

🌼 前端宝藏小哥哥