Axios

看了下内部招呼文档的文章,突发奇想想看下axios的一些内容

Axios的原理

其实axios是基于XMLHttpRequesthttp(s)封装

  • 浏览器环境下,通过以下语句判断
const isXHRAdapterSupported = typeof XMLHttpRequest !== 'undefined';
  • node环境下,通过以下语句判断
const isHttpAdapterSupported = typeof process !== 'undefined' && utils.kindOf(process) === 'process';

在使用过程,用户可以自己传入一个适配器adapter,如果不传入,就默认使用以上两个适配器

// `adapter` allows custom handling of requests which makes testing easier.
// Return a promise and supply a valid response (see lib/adapters/README.md).
adapter: function (config) {
  /* ... */
},

简易的XMLHttpRequest封装

基于axios的写法,我们自己写一个XMLHttpRequest调用过程

/**
 * 以下代码复制到浏览器控制台执行
 * 
 * 链接是我通过apifox启动的本地mock链接,也就返回一个对象
 * 
 * {"sex":"woman","age":72,"name":"分进传心效"}
 */

const request = new XMLHttpRequest()

request.open('GET', '   http://127.0.0.1:4523/m1/2638603-0-default/user')

function onloadend() {
  const response = request.responseText

  console.log(response);
}

if('onloadend' in request) {
  request.onloadend = onloadend
} else {
  request.onreadystatechange = () => {
    if (!request || request.readyState !== 4) {
      return;
    }
    // readystate handler is calling before onerror or ontimeout handlers,
    // so we should call onloadend on the next 'tick'
    setTimeout(onloadend);
  }
}

request.send()

简易的http(s)封装

这个暂时搁置,有点不想写

axios在项目中的使用

在公司中参与的项目基本上都是直接或间接使用axios

  • 管理系统大多是直接使用axios
  • 基于umi的系统中使用的umi的request插件,也是基于axios封装
  • 手机银行项目中的前端框架的请求方法this.$ajax也是基于axios封装

不过我个人项目的话,会更倾向于使用fetch封装的请求方法,比如nuxtofetchsindresorhusky

ky

ky能在node和浏览器环境下使用,基本使用很简单

ky.get(input, options?)
ky.post(input, options?)
ky.put(input, options?)
ky.patch(input, options?)
ky.head(input, options?)
ky.delete(input, options?)

如果希望进行封装,可以

import ky from 'ky';

const api = ky.extend({
    hooks: {
        beforeRequest: [
            request => {
                request.headers.set('X-Requested-With', 'ky');
            }
        ]
    }
});

const response = await api.get('https://example.com/api/users');

也提供了ky.create创建新实例,与extend的区别是,create返回新的ky实例,extend是在原来ky实例上新增属性

有几个比较有用的配置

prefixUrl
import ky from 'ky';

// On https://example.com

const response = await ky('unicorn', {prefixUrl: '/api'});
//=> 'https://example.com/api/unicorn'

const response2 = await ky('unicorn', {prefixUrl: 'https://cats.com'});
//=> 'https://cats.com/unicorn'
beforeRequest
// 对标axios的interceptor.request

import ky from 'ky';

const api = ky.extend({
    hooks: {
        beforeRequest: [
            request => {
                request.headers.set('X-Requested-With', 'ky');
            }
        ]
    }
});

const response = await api.get('https://example.com/api/users');
afterResponse
// 对标axios的interceptor.response

import ky from 'ky';

const response = await ky('https://example.com', {
    hooks: {
        afterResponse: [
            (_request, _options, response) => {
                // You could do something with the response, for example, logging.
                log(response);

                // Or return a `Response` instance to overwrite the response.
                return new Response('A different response', {status: 200});
            },

            // Or retry with a fresh token on a 403 error
            async (request, options, response) => {
                if (response.status === 403) {
                    // Get a fresh token
                    const token = await ky('https://example.com/token').text();

                    // Retry with the token
                    request.headers.set('Authorization', `token ${token}`);

                    return ky(request);
                }
            }
        ]
    }
});
retry
/**
 * retry
  Type: object | number
  Default:

  limit: 2
  methods: get put head delete options trace
  statusCodes: 408 413 429 500 502 503 504
  maxRetryAfter: undefined
  backoffLimit: undefined
 */

import ky from 'ky';

const json = await ky('https://example.com', {
    retry: {
        limit: 10,
        methods: ['get'],
        statusCodes: [413],
        backoffLimit: 3000
    }
}).json();
Cancel
// 使用fetch内置的取消请求方法
import ky from 'ky';

const controller = new AbortController();
const {signal} = controller;

setTimeout(() => {
    controller.abort();
}, 5000);

try {
    console.log(await ky(url, {signal}).text());
} catch (error) {
    if (error.name === 'AbortError') {
        console.log('Fetch aborted');
    } else {
        console.error('Fetch error:', error);
    }
}

ofetch

nuxt的内部团队unjs孵化的小项目,内置在nuxt中,也是能在node和浏览器中使用,在workers中也能使用

可以理解成是一个增强版的fetch

基本使用
// get
await ofetch('/movie?lang=en', { query: { id: 123 } })
// post
const { users } = await ofetch('/api/users', { method: 'POST', body: { some: 'json' } })
封装
const apiFetch = ofetch.create({ baseURL: '/api' })

apiFetch('/test') // Same as ofetch('/test', { baseURL: '/api' })

const get = (options) => apiFetch('/api/users', { method: 'GET', ...options })
const post = (options) => apiFetch('/api/users', { method: 'POST', ...options })
retry
await ofetch('http://google.com/404', {
  retry: 3
})
Interceptors
await ofetch('/api', {
  async onRequest({ request, options }) {
    // Log request
    console.log('[fetch request]', request, options)

    // Add `?t=1640125211170` to query search params
    options.query = options.query || {}
    options.query.t = new Date()
  },
  async onRequestError({ request, options, error }) {
    // Log error
    console.log('[fetch request error]', request, error)
  },
  async onResponse({ request, response, options }) {
    // Log response
    console.log('[fetch response]', request, response.status, response.body)
  }  async onResponseError({ request, response, options }) {
    // Log error
    console.log('[fetch response error]', request, response.status, response.body)
  }
})
Updated on 4/23/2023