axios 的简易封装

axios 的简易封装

一些简单的封装,主要总结一下 interceptors 的使用。

初步封装

这一步主要是添加 response 的 interceptors,这里只是处理了当状态为 200 时,将直接返回 data.data,这样在其他地方获取数据的时候就不需要进行解构。

在没有用 interceptors 之前,解构确实挺烦的。

base url 这种我就直接放在这里了,不过正常情况下一般都是配置到 dotenv 里面去的,然后根据环境选择对应的 base url 之类的,这里偷懒一下

代码如下:

import axios from 'axios'

const instance = axios.create({
    baseURL: 'https://dummyjson.com/',
    timeout: 1000,
})

instance.interceptors.request.use(req => {
    return req
}, err => { })

instance.interceptors.response.use(res => {
    const { data, status } = res;
    if (status === 200)
        return data;
}, err => { })

export default instance;

这个是 React 部分的代码:

  const fetchProduct = async () => {
    const data = await axios.get('products/1')
    console.log(data);
  }

  useEffect(() => {
    fetchProduct()
  }, [])

浏览器渲染:

axios 的简易封装_第1张图片

对 CRUD 的封装

CRUD 部分主要还是对 API 调用进行封装,这一块可以根据使用的 UI 库不同进行微调,主要的作用是:

  • 在 API 调用之前使用遮罩显示 loading 状态

  • 在 API 调用结束后关闭遮罩

    loading 状态这一部分可以结合 Redux 或是 RxJS(我们项目目前正在使用的就是 RxJS),亦或者可以直接通过 UI 库完成

  • 返回一个 Promise 让其他地方继续调用

  • 对于错误的处理

这一部分可以做的很多,根据业务需求调整即可。

对于调用方面其实差距并不大,只不过是讲一些可以 centralize 的处理集中到了一个函数去处理,省的到处复制黏贴。

代码如下:

import axios from 'axios'

const instance = axios.create({
    baseURL: 'https://dummyjson.com/',
    timeout: 1000,
})

instance.interceptors.request.use(req => {
    return req
}, err => { })

instance.interceptors.response.use(res => {
    const { data, status } = res;
    if (status === 200)
        return data;
}, err => { })

const request = (url, params, options, method) => {
    // maybe add loading state here
    return new Promise((resolve, reject) => {
        let data;
        if (method === 'get') {
            data = { params };
        } else if (method === 'post') {
            data = { data: params }
        }

        instance({
            method,
            url,
            data: {
                firstName: 'Fred',
                lastName: 'Flintstone'
            }
        }).then(res => {
            resolve(res);
        }).catch(e => {
            // sent some notification
            console.log(e);
        }).finally(() => {
            // may be remove loading state
        })
    })
}

export const get = (url, params, options) => {
    return request(url, params, options, 'get')
}

export default instance;

React 部分其实没有什么很大的变动:

  const fetchProduct = async () => {
    // const data = await axios.get('products/1')
    // console.log(data);
    const data = await get('products/1');
    console.log(data);
  }

从结果上来说是看不出来有什么区别的:

2nd wrapper

三次封装

三次封装主要就是使用一下 interceptors 了。

在这一部分会添加一个 POST 的调用,并且在 interceptors 中判断调用的方法,如果是 POST 或者 PUT 就添加 header。

在 interceptors 中修改 header、token 是一个非常常见的业务逻辑。另外一个处理方法就是在登录之后重新建立一个新的 instance,这两种处理方法都是比较常见的业务情景。

我这里将原本的数组转化成了对象,并且传了两个 success 和 fail 参数,主要是因为我们目前的项目也没有很好的用到 RxJS 的特性(除了 subscribe 之外其他的也没有怎么用……),也没用 Redux,所以很多 callback 的处理就是传俩函数,在 then 和 catch 中进行处理。

如果是使用其他的库,设计好可复用性这种东西,success 和 fail 的存在并不是必须的。

代码如下:

import axios from 'axios'

const instance = axios.create({
    baseURL: 'https://dummyjson.com/',
    timeout: 1000,
})

const METHOD_TYPE = {
    POST: 'post',
    GET: 'GET',
    PUT: 'PUT'
}

instance.interceptors.request.use(req => {
    const { method } = req;
    if (method === METHOD_TYPE.POST || method === METHOD_TYPE.PUT)
        req.headers = {
            ...req.headers,
            'Content-Type': 'application/json'
        };

    return req
}, err => { })

instance.interceptors.response.use(res => {
    const { data, status } = res;
    if (status === 200)
        return data;
}, err => { })

const request = (url, params, options, method, success, fail) => {
    // maybe add loading here
    return new Promise((resolve, reject) => {
        let data;
        if (method === METHOD_TYPE.GET) {
            data = { params };
        } else if (method === METHOD_TYPE.POST) {
            data = { data: params }
        }

        instance({
            method,
            url,
            data: {
                firstName: 'Fred',
                lastName: 'Flintstone'
            }
        }).then(res => {
            success();
            resolve(res);
        }).catch(e => {
            fail()
            // sent some notification
            console.log(e);
        }).finally(() => {
            // may be remove loading state
        })
    })
}

export const get = ({ url, params, options, success, fail }) => {
    return request(url, params, options, METHOD_TYPE.GET, success, fail)
}

export const post = ({ url, params, options, success, fail }) => {
    return request(url, params, options, METHOD_TYPE.POST, success, fail)
}

export default instance;

React 调用:

  const fetchProduct = async () => {
    // const data = await axios.get('products/1')
    // console.log(data);
    const data = await get({
      url: 'products/1',
      success: () => {
        console.log('get success');
        // set data etc
      },
      fail: () => {
        console.log('get fail');
      }
    });
    console.log(data);

    const postData = await post({
      url: 'products/add', params: {
        title: 'BMW Pencil',
      },
      success: () => {
        // set data etc
        console.log('post success');
      },
      fail: () => {
        console.log('post fail');
      }
    })
    console.log(postData);
  }

这部分截图忘了,基本上就是 GET 和 POST 的两个输出。

最后封装

这一部分就是最近新折腾的取消调用,axios 的官方文档其实给的挺明确的,不过我想太多了……

最后还是试着跑了一下才发现是真的可以成功:

const controller = new AbortController();

instance.interceptors.request.use(req => {
    const { method } = req;
    if (method === METHOD_TYPE.POST || method === METHOD_TYPE.PUT)
        req.headers = {
            ...req.headers,
            'Content-Type': 'application/json'
        };

    if (method === METHOD_TYPE.POST) {
        return {
            ...req,
            signal: controller.signal
        }
    }

    return req
}, err => {})

controller.abort();

这部分的代码中,如果遇到了一些需要放弃调用的情况——如 token 已经过期等,就可以直接在 interceptors 中返回一个 AbortController 的 signal,controller.abort(); 会监听这个 signal,存在的情况下就会取消 API 调用。

cancelToken 已经过期了,官方并不推荐使用。

效果如下:

axios 的简易封装_第2张图片

axios 的简易封装_第3张图片

可以看到 React 组件中的 log 是输出了,但是 React 中并没有拿到任何的返回值。

network 中也可以看到没有 POST 的调用,这就可以说明 axios 在请求发送出去之前就已经将其取消了,而不是在发送出去之后关闭连接通道假装没有收到。

基本上来说比较常见的业务封装就是这些了,比较经常遇到的 cases 也差不多就这些,当然,有比较有意思的业务需求可以提出来一起讨论一下,如果解决方案还挺干净的我也可以存下来当作 boilerplate。

你可能感兴趣的:(#,React,javascript,前端,axios)