Axios, которыми вы, возможно, не умеете пользоваться

axios
  • В дополнение к чисто статическим страницам отображения в ежедневной фронтенд-разработке важно делать некоторые запросы к интерфейсу, отXMLHttpRequest,прибытьjQueryизajax, а затем позжеFetchа такжеAxios.

Почему вы выбираетеAxios

  • Создание XMLHttpRequests из браузера
  • Создать http-запрос из node.js
  • Обещанная поддержка API
  • Перехват запросов и ответов
  • Преобразование данных запроса и данных ответа
  • отменить запрос
  • Автоматически преобразовывать данные JSON
  • Поддержка клиентов против XSRF

почему бы нетFetch

  • Fetchtch сообщает об ошибках только сетевых запросов, 400 и 500 считаются успешными запросами, которые необходимо инкапсулировать для обработки.
  • По умолчанию Fetch не будет хранить файлы cookie, и вам необходимо добавить элементы конфигурации (также требуется для Axios).
  • Fetch не поддерживает прерывание и контроль тайм-аута.Контроль тайм-аута, реализованный с помощью setTimeout и Promise.reject, не предотвращает продолжение выполнения запроса в фоновом режиме.
  • fetch не имеет возможности отслеживать ход выполнения запроса

базовая конфигурация

Установить

# npm
$ npm install axios
# yarn
$ yarn add axios
# cdn
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

Использовать напрямую

1. Используйте.выполнить метод запроса,[method name]содержитrequest,head,get,put,delete,post,patch.

axios[method name](url[, data[, config]])

2. Используйте входящиеconfigвызов объекта

axios([config])
.then(response => response)
.catch(error => error)

// config 可选内容的配置选项。只有 url 是必需的。如果没有指定 method,请求将默认使用 get 方法。
{
  // `url` 是用于请求的服务器 URL
  url: '/user',

  // `method` 是创建请求时使用的方法
  method: 'get', // 默认是 get

  // `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
  // 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
  baseURL: 'https://some-domain.com/api/',
  
  // `headers` 是即将被发送的自定义请求头
  headers: {'X-Requested-With': 'XMLHttpRequest'},

  // `params` 是即将与请求一起发送的 URL 参数
  // 必须是一个无格式对象(plain object)或 URLSearchParams 对象
  params: {
    ID: 12345
  },
  ...,
  // `cancelToken` 指定用于取消请求的 cancel token
  // (查看后面的 Cancellation 这节了解更多)
  cancelToken: new CancelToken(function (cancel) {
  })

3. Параллельная обработка

axios.all(iterable)
axios.spread(callback)

function getUserAccount() {
  return axios.get('/user/12345');
}

function getUserPermissions() {
  return axios.get('/user/12345/permissions');
}

axios.all([getUserAccount(), getUserPermissions()])
  .then(axios.spread(function (acct, perms) {
    // 两个请求都执行完成
  }));

Создайте экземпляр, используя

  • пройти черезaxios.createСоздайте экземпляр запроса, который может получитьconfigобъект;
import axios from axios;
var instance = axios.create([config]);

По умолчаниюconfigнастроить

1. Настройте время ожидания запроса

# 使用defaults
axios.defaults.timeout = 30000 // 时间以毫秒为单位
# 使用实例传入config
var instance = axios.create({
    timeout: 30000 
});

2. Перенос запроса конфигурацииcookie

# 使用defaults
axios.defaults.withCredentials = true // true为自动携带
# 使用实例传入config
var instance = axios.create({
    withCredentials: true
});

3. КонфигурацияbaseURL

  • baseURLбудут автоматически добавлены вurlспереди, если толькоurlэто абсолютный URL.
# 使用defaults
axios.defaults.baseURL = 'https://github.com/detanx'
# 使用实例传入config
var instance = axios.create({
    baseURL: 'https://github.com/detanx'
});

4. Конфигурацияheaders

  • Например установкаtoken
# 使用defaults
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
# 使用实例传入config
var instance = axios.create({
    headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Authorization': AUTH_TOKEN
});

5. Отменить запросcancelToken

  • (1) ИспользованиеCancelToken.sourceСоздание фабричного методаcancelToken.
const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get(url, {
  cancelToken: source.token
}).catch(thrown => thrown);

// 取消请求(message 参数是可选的)
source.cancel('cancel.');
  • (2) Проходя черезexecutorфункционировать, чтобыCancelTokenконструктор для созданияcancelToken.
const CancelToken = axios.CancelToken;
let cancel;

axios.get(url, {
  cancelToken: new CancelToken(function executor(c) {
    // executor 函数接收一个 cancel 函数作为参数
    cancel = c;
  })
})

// 取消请求(message 参数是可选的)
cancel('cancel.');

6.Более

Конфигурация перехватчика и обработка ошибок

  • в запросе или ответеthenилиcatchПерехватите их перед обработкой.

запрос на перехват

  • вы можете добавить прямо вimportВойдитеaxiosВыше обычно рекомендуется создать новыйAxiosЭкземпляр, прикрепите значение по умолчанию и перехватчик к экземпляру.

1. Перехватывать повторные запросы

const NotCancleUrl = ['/stockserver/user/login-user']; // 不取消重复请求的url列表
const pendingRequestArray = []; // 声明一个数组用于存储每个请求的取消函数和请求标识
const { CancelToken } = axios;
const removePending = config => {
  for (const index in pendingRequestArray) {
    const key = `${config.url}&${config.method}`;
    if (pendingRequestArray[index].key === key && !NotCancleUrl.includes(config.url)) {
      // 当当前请求在数组中存在时执行函数体
      pendingRequestArray[index].cancel(); // 执行取消操作
      pendingRequestArray.splice(index, 1); // 把这条记录从数组中移除
    }
  }
};
// 添加请求拦截器
axios.interceptors.request.use( config => {
    // 例如添加取消重复请求
    removePending(config); // 在一个发送请求前执行一下取消操作
    config.cancelToken = new CancelToken(c => {
      // 这里的请求标识是用请求地址&请求方式拼接的字符串,你可以选择其他的一些方式
      pendingRequestArray.push({ key: `${config.url}&${config.method}`, cancel: c });
    });
    return config;
  }, function (error) {
    // 处理请求错误
    return Promise.reject(error);
  });

2. Перехватить адрес запроса и заменить переменные в адресе

  • С другими разработчиками согласовано, что переменные в адресе запроса передаются через{{varname}}Пакет, в параметре запроса передается переменная, соответствующая заменяемому значению, а путем замены переменной получается правильный адрес запроса.
/** 变量替换
/* requestUrl 含有变量的字符串
/* paramsObj 变量对象
/* return []
**/
const varsReplace = (requestUrl, paramsObj) => {
    const SYMBOL_REGEXP = /\{{(.+?)\}}/g;
    const VARS_ARRAY = requestUrl.match(SYMBOL_REGEXP);
    const newParamsObj = Object.assign({}, paramsObj);
    if (VARS_ARRAY){
        for (const variable of VARS_ARRAY){
            const varChartArray = variable.slice(2, -2);
            delete newParamsObj[varChartArray];
            const value = paramsObj[varChartArray];
            requestUrl = requestUrl.replace(variable, value);
        }
    }
    // 返回新的请求地址、剔除变量后的请求参数
    return [requestUrl, newParamsObj];
};
// 添加发起拦截器
instance.interceptors.request.use(
    config => {
        let param = {};
        param = (config.method === 'get') ? config.params : config.data;
        const [requestUrl, newParamsObj] = varsReplace(config.url, param);
        if (config.method === 'get') {
            config.params = newParamsObj;
        } else {
            config.data = newParamsObj;
        }
        config.url = requestUrl;
        return config;
    },
    error => Promise.reject(error)
);

3. Отсутствие перехвата подмены адреса по спецзапросам

  • Например, загрузка файла, мы передаемconfigсерединаheadersвнизContent-TypeОпределить формат содержания запроса, если онmultipart/form-dataмы просто вернемсяconfig, другие типы, которые не нуждаются в обработке, аналогичны.
// axios.js
instance.interceptors.request.use(
    config => {
+        const { headers, method, params, data, url } = config;
+        if (headers['Content-Type'] === 'multipart/form-data') {
+            return config;
+        }
        let param = {};
    }
)

Перехват ответа

1. Повторные запросы перехватываются в перехвате запросов выше.Если запрос прошел успешно, мы должны передать соответствующий запрос из запроса после успешного запроса.pendingRequestArrayУдалено из массива кешированных запросов.

// 添加响应拦截器
axios.interceptors.response.use(
  // 处理响应数据
  response => {
    removePending(response.config); // 请求成功后,从pendingRequestArray中移除
    return response;
  },
  // 处理响应错误
  error => {
    return Promise.reject(error);
  }
);

2. Тайм-аут для соответствующего запроса,401,403Состояние выполняет ту же обработку (конечно, вы также можете выполнить некоторую другую обработку в соответствии с фактическим приложением в вашем проекте).

axios.interceptors.response.use(
  response => response,
  error => {
    // 服务请求超时
    if (error === undefined || error.code === 'ECONNABORTED') {
      message.warning('服务请求超时');
      return Promise.reject(error);
    }
    const {
      response: { status }
    } = error;
    const { response } = error;
    const info = response.data;
    // 401未登录
    if (status === 401) {
      // 未登录去重定向登录页面
      ...
    }
    // 403访问被拒
    if (status === 403) {
      ...
    }
    return Promise.reject(error);
  }
)

обработка ошибок

1. по желаниюcatchпроцесс в методе

axios.get(url)
  .catch(function (error) {
    if (error.response) {
      const { data, status, headers } = error.response;
      // 请求已发出,但服务器响应的状态码不在 2xx 范围内
      console.log(data, status, headers);
    } else {
      // Something happened in setting up the request that triggered an Error
      console.log('Error', error.message);
    }
    console.log(error.config);
  });

2. ИспользуйтеvalidateStatusПараметры конфигурации определяют пользовательскийHTTPДиапазон ошибок для кодов состояния.

axios.get(url), {
  validateStatus: function (status) {
    return status < 500; // 状态码在大于或等于500时才会 reject
  }
})

приложение в проекте

  • Настройки по умолчанию и перехватчики обсуждались ранее, и ниже упоминается только инкапсуляция экземпляра метода запроса.

Пакет общего пользования

// axios.js
// 请求方法
export const AXIOS_METHOD_TYPE = {
  GET: 'GET',
  POST: 'POST',
  PUT: 'PUT',
  DELETE: 'DELETE',
  PATCH: 'PATCH'
};
/** 方法说明
 * @method request
 * @param api 请求相对地址
 * @param method 请求方法,默认get
 * @param params 请求参数 默认为空
 * @param config 请求配置 默认为空
 * @return function 返回axios实例
*/
const request = (api, method = AXIOS_METHOD_TYPE.GET, params = {}, config = {}) => {
    method = method.toLocaleUpperCase();
    // get请求放在params中,其他请求放在body
    const data = method === 'GET' ? 'params' : 'data';
    // 这部分也可以放到defaults中去设置
    let headers = {
        'X-Requested-With': 'XMLHttpRequest',
        'Content-Type': 'application/json'
    };
    if (config.headers) {
        headers = {
          ...headers,
          ...config.headers
        };
    }
    return axios({
        url: api,
        method,
        [data]: params,
        headers
    });
};
export default request;

Как использовать

// index.js
import request from 'request';
request('/user/login', post, {
    username: 'detanx',
    password: '123456'
}).then(response => console.log(response))
.catch(error => console.log(error))

hooksинкапсуляция вызова

// useRequest.js
import { useState, useEffect } from 'react';
// instance 为myAxios中暴露的添加了默认方法及拦截器的axios实例
import { instance, AXIOS_METHOD_TYPE } from './myAxios';

/** 方法说明
 * @method useRequest
 * @return [
        responseData, // 请求成功的返回
        errorData, // 请求失败返回
        loading, // 请求是否完成
        setRequestParams // 参数改变重新请求
    ] 
*/
const useRequest = (url, method = AXIOS_METHOD_TYPE.GET, params = {}, config = {}) => {
    method = method.toLocaleUpperCase();
    const data = method === 'GET' ? 'params' : 'data';
    const [responseData, setResponseData] = useState({});
    const [errorData, setErrorData] = useState({});
    const [loading, setLoading] = useState(true);
    const [requestParams, setRequestParams] = useState(params);
    let headers = {
        'X-Requested-With': 'XMLHttpRequest',
        'Content-Type': 'application/json'
    };
    if (config.headers) {
        headers = {
            ...headers,
            ...config.headers
        };
    }
    useEffect(() => {
        instance({
            url,
            method,
            [data]: requestParams,
            headers
        })
            .then(({ data }) => {
                setLoading(false);
                setResponseData(data);
            })
            .catch(err => {
                setLoading(false);
                setErrorData(err);
            });
    }, [requestParams]);
    return [
        responseData,
        errorData,
        loading,
        setRequestParams
    ];
};
export default useRequest;

Как использовать

// index.js
import useRequest from 'useRequest';
expoert default () => {
    const [
        responseData,
        errorData,
        loading,
        setRequestParams
    ] = useRequest('/api/user', 'get')
    const clickHandle = () => {
        setRequestParams({name: 'detanx'})
    }
    return (<>
        <button onClick={clickHandle}>新参数请求</button>
    </>)
}

hooksИдеальная упаковка

  • Вышеупомянутый пакет будет иметь проблему, то есть каждый раз, когда вы его используете.useRequestЗапрос будет инициирован напрямую.Некоторые страницы не требуют запроса при входе. Например, чистая страница входа должна инициировать запрос только при отправке информации для входа, поэтому приведенный выше пакет оптимизирован. соглашениеparamsдляfalseНе инициировать запрос.
// 修改params的默认值为false及默认不发送请求
const useRequest = (url, method = AXIOS_METHOD_TYPET.GET, params = false, config = {}) => {
    ...
    useEffect(() => {
        // 约定`params`为`false`不发起请求
+       if (requestParams === false) return;        
        instance()
    }, [requestParams]);
    ...
};

hooksУпаковка идеальных двоих

  • Когда запрос выполнен успешно, тот же запрос будет инициироваться позже. Если данные об успешном выполнении или данные о сбое, возвращенные дважды, совпадают, приведенный выше код не будет обновлен. так вuseRequestДобавьте следующий код в файл .
const useRequest = (url, method = AXIOS_METHOD_TYPET.GET, params = false, config = {}) => {
    ...
-    const [responseData, setResponseData] = useState({});
-    const [errorData, setErrorData] = useState({});
-    const [loading, setLoading] = useState(true); // 发起请求时再将loading设为true
+    const [responseData, setResponseData] = useState(null);
+    const [errorData, setErrorData] = useState(null);
+    const [loading, setLoading] = useState(false);
+    const reloadRequest = param => {
+        setResponseData(null); // 将成功数据重置为null
+        setErrorData(null); // 将失败数据重置为null
+        setRequestParams(param); // 发起新的请求
+    };
    useEffect(() => {
        if (requestParams === false) return;
        // 发起请求,loading设为true
+       setLoading(true);
        instance({})
        ...
    });
-   return [
+   return {
        responseData,
        errorData,
        loading,
-       setRequestParams
+       reloadRequest
-   ]
+   };
}
    
  • Возвращаем массив объекту.Преимущество массива в том, что он вынужден соответствовать каждому данным, а объект может брать нужные ему данные сам.

постскриптум

  • Достаточно большинства потребностей в ежедневной разработке.Если у вас есть другие потребности в вашем проекте, пожалуйста, оставьте сообщение.
  • Если вам было полезно, ставьте лайк 👍, спасибо за поддержку!