# 1.前言

本篇文章主要做一些项目的起手,编写代码实现axios最基本的操作,如下:

axios({
  method: "get",
  url: "/base/get",
  params: {
    a: 1,
    b: 2,
  },
});
1
2
3
4
5
6
7
8

主要目的是跑通整个流程,为后续的开发做准备。

# 2.创建入口文件

我们在src 目录下,先创建一个 index.ts 文件,作为整个库的入口文件,然后我们先定义一个 axios 方法,并把它导出,如下:

// src/index.ts
function axios(config) {}

export default axios;
1
2
3
4

在上面代码中,我们需要给 config 参数定义了一种接口类型。我们创建一个 types 目录,在下面创建一个 index.ts 文件,作为我们项目中公用的类型定义文件。

// types/index.ts
export interface AxiosRequestConfig {
  url: string;
  method?: string;
  data?: any;
  params?: any;
}
1
2
3
4
5
6
7

其中,url 为请求的地址,必选属性;而其余属性都是可选属性。method 是请求的 HTTP 方法;datapostpatch 等类型请求的数据,放到 request body 中的;paramsgethead 等类型请求的数据,拼接到 urlquery string 中的。

为了让 method 只能传入合法的字符串,我们定义一种字符串字面量类型 Method

export type Method =
  | "get"
  | "GET"
  | "delete"
  | "Delete"
  | "head"
  | "HEAD"
  | "options"
  | "OPTIONS"
  | "post"
  | "POST"
  | "put"
  | "PUT"
  | "patch"
  | "PATCH";
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

然后回到 src/index.ts,我们引入 AxiosRequestConfig 类型,作为 config 的参数类型,如下:

// src/index.ts
import { AxiosRequestConfig } from "./types";

function axios(config: AxiosRequestConfig) {}

export default axios;
1
2
3
4
5
6

OK,入口文件axios核心对象创建成功!

# 3. 封装原生 ajax 操作

axios的核心功能就是发送请求,所以我们必须将原生的ajax操作进行封装,后面将用它来发送所有请求。

我们在 src 目录下创建一个 xhr.ts 文件,我们导出一个 xhr 方法,它接受一个 config 参数,类型也是 AxiosRequestConfig 类型。

// src/xhr.ts
export default function xhr(config: AxiosRequestConfig): void {
  const { data = null, url, method = "get" } = config;

  const request = new XMLHttpRequest();

  request.open(method.toUpperCase(), url, true);

  request.send(data);
}
1
2
3
4
5
6
7
8
9
10

我们首先通过解构赋值的语法从 config 中拿到对应的属性值赋值给我的变量,并且还定义了一些默认值,因为在 AxiosRequestConfig 接口的定义中,有些属性是可选的。

接着我们实例化了一个 XMLHttpRequest 对象,然后调用了它的 open 方法,传入了对应的一些参数,最后调用 send 方法发送请求。

# 4. 引入 xhr 模块

编写好了 xhr 模块,我们就需要在 index.ts 中去引入这个模块,如下:

// src/index.ts
import { AxiosRequestConfig } from "./types";
import xhr from "./xhr";

function axios(config: AxiosRequestConfig): void {
  xhr(config);
}

export default axios;
1
2
3
4
5
6
7
8
9

OK,基本的发送请求代码完成,接下来,我们可以写一个简单的 demo,来验证一下我们刚才编写的axios对象能否使用。

# 5. 编写 demo

demo分为客户端和服务端:客户端用来发送请求;服务端用来响应请求。

# 5.1 编写服务端 server

服务端采用express库编写,在 server 目录下创建 server.js 文件:

const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const router = express.Router();

// 使用body-parser中间件
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

router.get("/api/base/get", function(req, res) {
  res.json({
    msg: `hello world`,
  });
});

app.use(router);

const port = process.env.PORT || 3000;
module.exports = app.listen(port, () => {
  console.log(`Server listening on http://localhost:${port}, Ctrl+C to stop`);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 5.2 编写客户端 examples

首先在项目根目录下创建 index.html,作为所有 demo 的入口文件。

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>ts-axios examples</title>
  </head>
  <body>
    <h1>ts-axios examples</h1>
    <ul>
      <li><a href="examples/base">Base</a></li>
    </ul>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13

然后在 examples 目录下创建 base 目录,作为本篇文章的 demo 目录,在该目录下再创建 index.htmlapp.ts 文件

index.html 文件如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Baseexample</title>
  </head>
  <body>
    <script src="/__build__/base.js"></script>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10

app.ts 文件如下:

import axios from "../../src/index";

axios({
  method: "get",
  url: "/api/base/get",
  params: {
    a: 1,
    b: 2,
  },
});
1
2
3
4
5
6
7
8
9
10

# 5.3 运行 demo

我们在命令行中执行:

# 同时开启客户端和服务端
npm run server | npm start
1
2

其中:

npm run server相当于执行了 node server/server.js,会开启我们的服务端。 npm start会开启我们的客户端。

接着我们打开 chrome 浏览器,访问 http://localhost:8000/ 即可访问我们的 demo 了,我们点击 Base ,通过F12network 部分我们可以看到成功发送到了一条请求,并在 response 中看到了服务端返回的数据。

至此,我们就实现了一个简单的请求发送,并编写了相关的 demo。但是现在存在一些问题:我们传入的 params 数据并没有用,也没有拼接到 url 上;我们对 request body 的数据格式、请求头 headers 也没有做处理;另外我们虽然从网络层面收到了响应的数据,但是我们代码层面也并没有对响应的数据做处理。那么下面一篇文章,我们就来解决这些问题。