当前位置 : 首页 > 购买指南

Axios默认配置实现:全局与实例配置合并策略

<|begin▁of▁sentence|># 1. 前言 在上一篇文章中,我们实现了`axios`的基础功能,但到目前为止,我们都只是实现了一个很简单的`axios`,一个最最基础的`axios`,我们还有很多功能没有实现,比如:`axios`的默认配置、拦截器、`xsrf`防御、上传下载进度监控等等。那么从本篇文章开始,我们就来一点一点完善这些功能。 我们先从`axios`的默认配置开始,用过`axios`的同学都知道,`axios`提供了一些默认配置,用户可以选择性的设置一些配置,并且这些配置可以分为两类: - 全局默认配置:`axios.defaults.xxx` - 实例默认配置:`axios.create({xxx})` 那么,接下来,我们就为我们的`axios`添加上默认配置功能。 # 2. 默认配置 ## 2.1 修改类型定义 由于我们要给`axios`添加默认配置,所以我们需要先修改之前定义的类型。 首先,我们创建一个`src/types/config.ts`文件,在该文件中定义配置对象的类型。 ```typescript export interface AxiosRequestConfig { url?: string; method?: Method; data?: any; params?: any; headers?: any; responseType?: XMLHttpRequestResponseType; timeout?: number; } ``` 然后,为了让`axios`支持默认配置,我们还需要给`axios`接口添加默认配置属性,修改`src/types/index.ts`: ```typescript import { AxiosRequestConfig } from "./config"; // 新增 export interface Axios { defaults: AxiosRequestConfig; request(config: AxiosRequestConfig): AxiosPromise; get(url: string, config?: AxiosRequestConfig): AxiosPromise; delete(url: string, config?: AxiosRequestConfig): AxiosPromise; head(url: string, config?: AxiosRequestConfig): AxiosPromise; options(url: string, config?: AxiosRequestConfig): AxiosPromise; post(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise; put(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise; patch(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise; } ``` 另外,我们还需要修改`AxiosInstance`接口,因为当我们使用`axios`的时候,不仅可以`axios(config)`,还可以`axios(url, config)`,并且还可以有默认配置,所以修改如下: ```typescript export interface AxiosInstance extends Axios { (config: AxiosRequestConfig): AxiosPromise; (url: string, config?: AxiosRequestConfig): AxiosPromise; } ``` ## 2.2 添加默认配置 默认配置我们可以在`src/defaults.ts`文件中实现: ```typescript 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; ``` 我们定义了 `defaults` 常量,它包含了一些默认的配置,我们在下一节就会把这些默认配置设置到 `axios` 上。其中 `method` 默认为 `get`;`timeout` 默认为 `0`;`headers` 默认包含一个 `common` 的对象, common 里包含 `Accept`属性,另外,对于不同的请求方法,`headers` 配置不同:对于没有 `data` 的请求方法,我们不需要设置 `Content-Type`;而对于有 `data` 的请求方法,我们将 `Content-Type` 默认设置为 `application/x-www-form-urlencoded`。 ## 2.3 设置默认配置 给`axios`对象添加`defaults`属性,让用户可以通过`axios.defaults.xxx`来获取或者设置默认配置。 修改`src/core/Axios.ts`: ```typescript import { AxiosRequestConfig, AxiosPromise, AxiosResponse } from "../types"; import { xhr } from "./xhr"; import { buildURL } from "../helpers/url"; import { transformRequest, transformResponse } from "../helpers/data"; import { processHeaders } from "../helpers/headers"; import defaults from "../defaults"; export default class Axios { defaults: AxiosRequestConfig; constructor(initConfig: AxiosRequestConfig) { this.defaults = initConfig; } request(config: AxiosRequestConfig): AxiosPromise { // 合并配置 config = mergeConfig(this.defaults, config); config.method = config.method.toLowerCase(); // 省略其他代码 } // 省略其他方法 } ``` 我们给`Axios`类添加了`defaults`成员属性,并且让`Axios`的构造函数接受一个`initConfig`参数,作为用户传入的初始配置,然后合并默认配置和初始配置。 另外,在`request`方法中,我们通过`mergeConfig`函数将默认配置`this.defaults`和用户传入的配置`config`做合并。 `mergeConfig`函数我们在`src/core/mergeConfig.ts`中实现: ```typescript import { AxiosRequestConfig } from "../types"; export default function mergeConfig( defaultConfig: AxiosRequestConfig, userConfig?: AxiosRequestConfig ): AxiosRequestConfig { const config = Object.create(null); userConfig = userConfig || {}; for (let key in userConfig) { mergeField(key); } for (let key in defaultConfig) { if (!userConfig[key]) { mergeField(key); } } function mergeField(key: string): void { config[key] = userConfig[key] || defaultConfig[key]; } return config; } ``` 合并的方式并不是简单的把这两个对象合并,而是根据不同的策略进行合并,不过目前我们还没有添加合并策略,所以目前我们只是简单的用用户配置覆盖默认配置,如果用户配置没有传入,那么就用默认配置。 ## 2.4 配置初始化 在创建`axios`对象的时候,我们需要传入默认配置,所以修改`src/axios.ts`: ```typescript import { AxiosInstance, AxiosRequestConfig } from "./types"; import Axios from "./core/Axios"; import defaults from "./defaults"; import { extend } from "./helpers/util"; function createInstance(config: AxiosRequestConfig): AxiosInstance { const context = new Axios(config); const instance = Axios.prototype.request.bind(context); extend(instance, context); return instance as AxiosInstance; } const axios = createInstance(defaults); export default axios; ``` 在`index.ts`入口文件中,我们引入`./defaults`默认配置,然后调用`createInstance`函数创建`axios`对象的时候把默认配置传进去,这样就能实现默认配置了。 OK,默认配置添加好了,我们接下来在`demo`中测试下效果。 # 3. demo 编写 在 `examples` 目录下创建 `defaults` 目录,在 `defaults` 目录下创建 `index.`: ``` < lang="en"> Defaults example ``` 再创建 `app.ts` 作为入口文件: ```typescript import axios from "../../src/index"; axios({ url: "/api/axios", method: "post", data: { msg: "hi", }, }); axios({ url: "/api/axios", method: "post", data: { msg: "hi", }, headers: { "Content-Type": "text/plain;charset=utf-8", }, }); ``` 接着在 `server/server.js` 添加新的接口路由: ```javascript // 添加默认配置接口 router.post("/api/axios", function(req, res) { res.json({ msg: "hello world", }); }); ``` 最后在根目录下的`index.`中加上启动该`demo`的入口: ```
  • defaults
  • ``` OK,我们在命令行中执行: ```bash # 同时开启客户端和服务端 npm run server | npm start ``` 接着我们打开 `chrome` 浏览器,访问 即可访问我们的 `demo` 了,我们点击 `defaults`,通过`F12`的 `network` 部分我们可以看到发送了两个请求,并且这两个请求都配置了默认的请求头,第一个请求我们没配置`headers`,所以使用了默认的`Content-Type:application/x-www-form-urlencoded`;第二个请求我们配置了`headers`,所以使用了我们配置的`Content-Type:text/plain;charset=utf-8`。 ![](~@/axios/06/01.png) ![](~@/axios/06/02.png) OK,默认配置功能已经实现了!

    栏目列表