导读

  • 这里主要实现了
    • 构建axios对象和函数
    • 发送网络请求:ajax
    • 拦截器
    • 适配器
    • CancolToken功能

axios

index.html

  • 这里直接使用esmodule, 需要注意import时候不能省略 .js
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    <body>
    <button> 发送请求 </button>
    <script type="module">
    import axios from './index.js'
    // fetch('./db.json').then(async (res) => console.log(await res.json()))

    axios.interceptors.request.use(function one(config) {
    console.log('请求拦截器 成功 - 1号');
    return config;
    }, function one(error) {
    console.log('请求拦截器 失败 - 1号');
    return Promise.reject(error);
    });

    axios.interceptors.request.use(function two(config) {
    console.log('请求拦截器 成功 - 2号');
    return config;
    }, function two(error) {
    console.log('请求拦截器 失败 - 2号');
    return Promise.reject(error);
    });

    axios.interceptors.response.use(function (response) {
    console.log('响应拦截器 成功 1号');
    return response;
    }, function (error) {
    console.log('响应拦截器 失败 1号')
    return Promise.reject(error);
    });

    axios.interceptors.response.use(function (response) {
    console.log('响应拦截器 成功 2号')
    return response;
    }, function (error) {
    console.log('响应拦截器 失败 2号')
    return Promise.reject(error);
    });

    const btns = document.querySelectorAll('button')
    let cancel = null;
    btns[0].onclick = function () {
    if (cancel !== null) {
    cancel();
    }
    let cancelToken = new axios.CancelToken(
    function (c) { cancel = c });
    axios({
    method: 'GET',
    url: 'http://localhost:3000/posts',
    // url: './db.json',
    cancelToken: cancelToken
    }).then(response => {
    cancel = null;
    console.log(response.data);
    })
    }
    </script>
    </body>

index.js

  • 导出构造函数
    1
    2
    import axios from './Axios.js'
    export default axios

Axios.js

  • 构建了Axios的类,并创造了他的实例对象
  • 此时就可以 axios.get or axios({method:"GET"})
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    import { request } from './request.js'
    import { CancelToken } from './CancelToken.js'
    import { InterceptorManager } from './interceptor.js'
    class Axios {
    constructor(config) {
    this.defaults = config;
    this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager(),
    }
    }
    request(config) {
    return request.call(this, config)
    }
    get(config) {
    return this.request({ method: "GET" })
    }
    post() {
    return this.request({ method: "POST" })
    }
    }
    function createInstance(config) {
    let context = new Axios(config);
    let instance = Axios.prototype.request.bind(context);
    let functionNamesArray = Object.getOwnPropertyNames(Axios.prototype)
    functionNamesArray.forEach(item => {
    instance[item] = Axios.prototype[item].bind(context)
    })
    Object.keys(context).forEach(key => {
    instance[key] = context[key];
    });
    return instance
    }
    let axios = createInstance()
    axios.CancelToken = CancelToken
    export default axios

dispatchRequest.js

  • 这里用于调用适配器
    1
    2
    3
    4
    5
    6
    7
    8
    import { xhrAdapter } from './xhrAdapter.js'
    export function dispatchRequest(config) {
    return xhrAdapter(config).then(response => {
    return response;
    }, error => {
    throw error;
    });
    }

xhrAdapter.js

  • 适配器:用于切换使用 node 版本还是 js 版本
  • 这里指写了 js 版本
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    export function xhrAdapter(config) {
    return new Promise((resolve, reject) => {
    let xhr = new XMLHttpRequest();
    xhr.open(config.method, config.url);
    xhr.send();
    xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
    if (xhr.status >= 200 && xhr.status < 300) {
    resolve({
    config: config,
    data: xhr.responseText,
    headers: xhr.getAllResponseHeaders(),
    request: xhr,
    status: xhr.status,
    statusText: xhr.statusText
    });
    } else {
    reject(new Error('请求失败 失败的状态码为' + xhr.status));
    }
    }
    }
    if (config.cancelToken) {
    config.cancelToken.promise.then(value => {
    xhr.abort();
    reject(new Error('请求已经被取消'))
    });
    }
    });
    }

interceptor.js

  • 拦截器的实例对象
    1
    2
    3
    4
    5
    6
    7
    8
    export class InterceptorManager {
    constructor() {
    this.handlers = [];
    }
    use(fulfilled, rejected) {
    this.handlers.push({ fulfilled, rejected })
    }
    }

request.js

  • 这里是关于拦截器的核心代码
  • 可以看出是用一个数组维护 响应拦截器 和 请求拦截器
  • 放的时候 请求拦截器 unshift , 相应拦截器 push
  • 取的时候 请求拦截器 shift , 相应拦截器 shift
  • 因此当多个拦截器执行的时候 请求拦截器的顺序是反的 相应拦截器的正的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import { dispatchRequest } from './dispatchRequest.js'
    export function request(config) {
    let promise = Promise.resolve(config);
    let chains = [dispatchRequest, undefined];
    this.interceptors.request.handlers.forEach(item => {
    chains.unshift(item.fulfilled, item.rejected)
    })
    this.interceptors.response.handlers.forEach(item => {
    chains.push(item.fulfilled, item.rejected);
    });
    while (chains.length) {
    promise = promise.then(chains.shift(), chains.shift());
    }
    return promise;
    }

CancelToken.js

  • 闭包的经典应用
  • promise的控制权交于外界 使用者控制
  • 使用者一旦调用方法 promise就可以自动触发 promise.resolve
  • 随后只要在 xhr 网络请求中 调用 xhr.abort() 来中断请求
    1
    2
    3
    4
    5
    6
    7
    export function CancelToken(executor) {
    var resolvePromise;
    this.promise = new Promise((resolve) => {
    resolvePromise = resolve
    });
    executor(() => resolvePromise());
    }

参考链接