基於 TS 實現 axios(六)

這一章主要實現一些配置化,讓用戶可以進行配置

  • 默認配置
  • transformRequest 和 transformResponse
  • axios.create API

目錄:

在這裏插入圖片描述

默認配置

添加 defaults.ts

import {AxiosRequestConfig} from './types';

const defaults: AxiosRequestConfig = {
    method: 'get',
    timeout: 0,
    headers:{
        common: {
            Accept: 'application/json, text/plain, */*',
        }
    },
}

const methodsNoData = ['delete','get','head','options'];

methodsNoData.forEach(method => {
    defaults.headers[method] = {}
})

const methodsWithData = ['post','put','patch'];

methodsWithData.forEach(method => {
    defaults.headers[method] = {
        'Content-Type': 'application/x-www-form-urlencoded',
    }
})
 
export default defaults;

這個文件主要是定義一些默認配置。

添加 core/mergeConfig.ts 文件

import { AxiosRequestConfig } from '../types';
import { isPlainObject,deepMerge } from '../helpers/util';

const strats = Object.create(null);

function defaultStart(val1:any,val2:any):any {
    return typeof(val2) !== 'undefined' ? val2: val1;
}
function formval2Start(val1:any,val2:any):any {
    if(typeof val2 !== 'undefined') {
        return val2;
    }
}

function deepMergeStrat(val1: any,val2:any): any {
    debugger;

    if(isPlainObject(val2)) {
        return deepMerge(val1,val2);
    } else if(typeof val2 !== 'undefined') {
        return val2
    } else if(isPlainObject(val1)) {
        return deepMerge(val1);
    } else if(typeof val1 !== 'undefined') {
        return val1;
    }
}

const stratKeysFromVal2 = ['url','params','data'];

stratKeysFromVal2.forEach(key => {
    strats[key] = formval2Start
})

const stratKyesDeepMerge = ['headers']

stratKyesDeepMerge.forEach(key => {
    strats[key] = deepMergeStrat;
})


export default function mergeConfig(config1:AxiosRequestConfig,config2?:AxiosRequestConfig):AxiosRequestConfig{
    if(!config2) {
        config2 = {}
    }
   
    const config = Object.create(null);

    for(let key in config2) {
        mergeField(key); 
    }

    for(let key in config1) {
        if(!config2[key]) {
            mergeField(key);
        }
    } 

    function mergeField(key: string):void {
        const strat = strats[key] || defaultStart
        config[key] = strat(config1[key],config2![key]);
    }

    return config;
} 

這個文件主要把默認配置和用戶自己配置的進行合併。

這裏定義了幾種合併方式,其中 url、params、data 採用 stratKeysFromVal2 方法,主要意思是:只要 val2 有數據,就返回 val2 ; headers 採用的是 deepMergeStrat 方法,主要是進行深拷貝,將 val1val2 合併成一個對象;其他就採用 defaultStart ,主要是:如果 val2 有數據,就返回 val2 ,否則返回 val1

helpers/util.ts 中新添加了一個方法 deepMerge :

// ...
export function deepMerge(...objs: any[]) : any {
    const result = Object.create(null);

    objs.forEach(obj => {
        if(obj) {
            Object.keys(obj).forEach(key => {
                const val = obj[key];
                if(isPlainObject(val)) {
                    if(isPlainObject(result[key])) {
                        result[key] = deepMerge(result[key],val)
                    } else {
                        result[key] = deepMerge(val);
                    }
                } else {
                    result[key] = val
                }
            })
        }
    })
    return result;
}

這個函數實現的是 深拷貝

core/Axios.ts 添加兩句話:

// ...
import mergeConfig from './mergeConfig';
// ...
request(url: any, config?:any):AxiosPromise {
    if(typeof url === 'string') {
        if(!config) {
            config = {}
        }
        config.url = url;
    } else {
        config = url;
    }
    config = mergeConfig(this.defaults, config);	// 添加一句
    // ...
}

core/dispatchRequest.ts 添加兩句話:

// ..
import { flattenHeaders } from '../helpers/header';
// ..
function processConfig(config: AxiosRequestConfig): void {
	// ...
    config.headers = flattenHeaders(config.headers,config.method!);
}   

helpers/header.ts 添加一個函數:

import { isPlainObject, deepMerge } from "./util";
// ..
export function flattenHeaders(headers: any, method: Method) : any {
    if(!headers) {
        return headers
    }
    headers = deepMerge(headers.common,headers[method],headers);

    const methodsToDelete = ['delete','get','head','options','post','put','patch','common'];

    methodsToDelete.forEach(method => {
        delete headers[method]
    })

    return headers;
}

defaults.ts 的配置進行 合併,刪除 delete,get,common 等屬性,規範話 Header

這樣 默認 配置寫完了

transformRequest 和 transformResponse

types/index.ts 最後添加:

// ..
export interface AxiosRequestConfig {
	// ..

    transformRequest?: AxiosTransformer | AxiosTransformer[],
    transformResponse?: AxiosTransformer | AxiosTransformer[],
	// ..
}
// ..
export interface AxiosTransformer {
    (data:any,headers?:any) : any,
}

新增 core/transform.ts 文件

import { AxiosTransformer } from "../types";

export default function transform(data:any,headers: any,fns:AxiosTransformer | AxiosTransformer[]) : any {
    if(!fns) {
        return data
    }
    if(!Array.isArray(fns)){
        fns = [fns];
    }
    fns.forEach(fn => {
        data = fn(data,headers);  
    })
    return data;
}

defaults.ts 添加

// ...
import {processHeaders} from './helpers/header'
import {transformRequest,transformResponse} from './helpers/data';
const defaults: AxiosRequestConfig = {
	// ...
    transformRequest: [
        function(data:any,headers:any):any {
            processHeaders(data,headers);
            return transformRequest(data);
        }
    ],
    transformResponse: [
        function(data: any):any {
            return transformResponse(data)
        }],
}
// ..

這個文件將以前寫過的 processHeaderstransformRequesttransformResponsetransformRequest 和 transformResponse 中進行調用。

修改過後的 core/dispatchRequest.ts 文件

import {AxiosRequestConfig,AxiosPromise, AxiosResponse} from '../types';
import xhr from './xhr';
import { buildURL } from '../helpers/url';
import { flattenHeaders } from '../helpers/header';

import transform from './transform';

export default function dispatchRequst (config :AxiosRequestConfig) : AxiosPromise {
    processConfig(config);
    return xhr(config).then(res => {
        return transformResponseData(res);
    });
}

function processConfig(config: AxiosRequestConfig): void {
    config.url = transformURL(config);
    config.data = transform(config.data,config.headers,config.transformRequest!);
    config.headers = flattenHeaders(config.headers,config.method!);
}    

function transformURL (config: AxiosRequestConfig) :string {
    const {url,params} = config;
    return buildURL(url!,params);
}

function transformResponseData(res:AxiosResponse):AxiosResponse {
    res.data = transform(res.data,res.headers,res.config.transformResponse!);
    return res;
}

這樣就寫完了。

axios.create

types/index.ts 新添加一個接口

// ...
export interface AxiosStatic extends AxiosInstance {
    create(config?:AxiosRequestConfig): AxiosInstance
}
// ...

axios.ts 文件中把 AxiosInstance 修改成 AxiosStatic 添加 create API 接口

import {AxiosStatic, AxiosRequestConfig} from './types';
import Axios from './core/Axios';
import {extend} from './helpers/util';
import defaults from './defaults';
import mergeConfig from './core/mergeConfig';

function createInstance(config:AxiosRequestConfig):AxiosStatic{
    const context = new Axios(config);
    const instance = Axios.prototype.request.bind(context); // 綁定 作用域
    extend(instance,context);

    return instance as AxiosStatic;
}

const axios = createInstance(defaults);

axios.create = function create(config) {
     return createInstance(mergeConfig(defaults,config));
}

export default axios;

這一章就寫到這裏了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章