ajax

AJAX:Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)

1. 初识 AJAX

1.1 XML 简介(已被 JSON 替代)

XML:Extensible Markup Language(可扩展标记语言)。被设计用于传输和存储数据


1.2 AJAX 优缺点

优点

  1. 无需刷新页面与服务器进行通信
  2. 允许根据用户事件来更新部分页面内容

缺点

  1. 没有浏览历史,不能回退
  2. 存在跨域问题(同源)
  3. SEO 不友好

1.3 AJAX + HTTP 协议请求报文与响应文本结构

HTTP(hypertext transport protocolo)协议(超文本传输协议),协议详细规定了浏览器和万维网服务器之间互相通信的规则

请求报文

  1. 请求行:
    • GET/POST/...:请求方法
    • /s?ie=utf-8:url
    • HTTP/1.1
  2. 请求头:
    • Host: atguigu.com
    • Cookie: name=guigu
    • Content-type: application/x-www-form-urlencoded
    • User-Agent: chrome 84
  3. 空行
  4. 请求体(get 时为空,post 时可以为:username=admin&password=admin

响应报文

  1. 响应行:
    • HTTP/1.1
    • 200:状态码
    • OK:状态字符串
  2. 响应头:
    • Content-type: text/html;charset=utf-8
    • Content-length: 2048
    • Content-encoding: gzip
  3. 空行
  4. 响应体:
<html>
  <head>
  </head>
  <body>
    <h1>响应体</h1>
  </body>
</html>
1
2
3
4
5
6
7

2. AJAX 基础

2.1 安装 express

  1. yarn init:初始化项目
  2. yarn add express:安装 express 框架

xx.js

// 1.引入 express
const express = require('express')

// 2.创建应用对象
const app = express()

// 3.创建路由规则
// request 是对请求报文的封装
// response 是对响应报文的封装
app.get('/', (request, response) => {
  // 设置响应体
  response.send('hello express')
})

// 4.监听端口启动服务
app.listen(8000, () => {
  console.log('服务已经启动,8000 端口监听中')
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  1. 执行:node xx.js
  2. 访问:localhost:8000 就能访问到了

2.2 请求基本操作

点击 '点击发送请求',会打印响应行、响应头和响应体

server.js

// 1.引入 express
const express = require('express')

// 2.创建应用对象
const app = express()

// 3.创建路由规则
// request 是对请求报文的封装
// response 是对响应报文的封装
app.get('/server', (request, response) => {
  // 设置响应头 设置允许跨域
  response.setHeader('Access-Control-Allow-Origin', '*')
  // 设置响应体
  response.send('hello ajax')
})

// 4.监听端口启动服务
app.listen(8000, () => {
  console.log('服务已经启动,8000 端口监听中')
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

get.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>AJAX</title>
  <style>
    #result {
      width: 200px;
      height: 100px;
      border: solid 1px purple;
    }
  </style>
</head>

<body>
  <button>点击发送请求</button>
  <div id="result"></div>

  <script>
    const btn = document.querySelector('button')
    const result = document.querySelector('#result')
    btn.addEventListener('click', function () {
      // 1.创建对象
      const xhr = new XMLHttpRequest()
      // 2.初始化,设置请求方法和 url
      xhr.open('GET', 'http://127.0.0.1:8000/server')
      // 3.发送
      xhr.send()
      // 4.事件绑定,处理服务端返回的结果
      // readystate 是 xhr 对象中的属性,表示状态。它有 5 个值:0 1 2 3 4
      // 0:未初始化;1:open 方法调用完毕;2:send 方法调用完毕;3:服务端返回部分结果;4.服务端返回全部结果
      xhr.addEventListener('readystatechange', function () {
        // 判断(服务端返回了所有的结果)
        if (xhr.readyState === 4) {
          // 判断响应的状态码:200 404 403 500
          // 2xx 成功
          if (xhr.status >= 200 && xhr.status < 300) {
            // 处理结果 行 头 空行 体
            // 1.响应行
            // console.log(xhr.status) // 状态码
            // console.log(xhr.statusText) // 状态字符串
            // console.log(xhr.getAllResponseHeaders()) // 响应头
            // console.log(xhr.response) // 响应体
            
            // 设置 result 的文本
            result.innerHTML = xhr.response
          }
        }
      })
    })
  </script>
</body>

</html>
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

2.3 设置请求参数

get.html

xhr.open('GET', 'http://127.0.0.1:8000/server?a=100&b=200&c=300')
1

点击发送后打开控制台里的网络,可以在负载中看到请求参数


2.4 发送 POST 请求

server.js:设置 post 的路由规则

// 1.引入 express
const express = require('express')

// 2.创建应用对象
const app = express()

// 3.创建路由规则
// request 是对请求报文的封装
// response 是对响应报文的封装
app.get('/server', (request, response) => {
  // 设置响应头 设置允许跨域
  response.setHeader('Access-Control-Allow-Origin', '*')
  // 设置响应体
  response.send('hello ajax')
})

app.post('/server', (request, response) => {
  response.setHeader('Access-Control-Allow-Origin', '*')
  response.send('hello ajax post')
})

// 4.监听端口启动服务
app.listen(8000, () => {
  console.log('服务已经启动,8000 端口监听中')
})
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

post.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    #result {
      width: 200px;
      height: 100px;
      border: solid 1px purple;
    }
  </style>
</head>

<body>
  <div id="result"></div>
  <script>
    const result = document.querySelector('#result')
    result.addEventListener('mouseover', function () {
      const xhr = new XMLHttpRequest()
      xhr.open('POST', 'http://127.0.0.1:8000/server')
      xhr.send()
      xhr.addEventListener('readystatechange', function () {
        if (xhr.readyState === 4) {
          if (xhr.status >= 200 && xhr.status < 300) {
            result.innerHTML = xhr.response
          }
        }
      })
    })
  </script>
</body>

</html>
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

2.5 POST 设置请求体

xhr.send('a=100&b=200&c=300')
1

2.6 设置请求头信息

post.html

xhr.open('POST', 'http://127.0.0.1:8000/server')

// 设置请求头
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
// 自定义请求头,必须在后端进行设置后才能得到响应,否则报错
xhr.setRequestHeader('name', 'yuanxinyue')
1
2
3
4
5
6

server.js

// 可以接收任意类型的请求
app.all('/server', (request, response) => {
  response.setHeader('Access-Control-Allow-Origin', '*')
  // 任何类型的请求头都可以接受(自定义的也行)
  response.setHeader('Access-Control-Allow-Headers', '*')
  response.send('hello ajax post')
})
1
2
3
4
5
6
7

2.7 服务端响应 JSON 数据

JSON.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    #result {
      width: 200px;
      height: 100px;
      border: solid 1px purple;
    }
  </style>
</head>

<body>
  <div id="result"></div>
  <script>
    const result = document.querySelector('#result')
    window.addEventListener('keydown', function () {
      const xhr = new XMLHttpRequest()
      // 设置响应体数据的类型
      xhr.responseType = 'json'
      xhr.open('GET', 'http://127.0.0.1:8000/json-server')
      xhr.send()
      xhr.addEventListener('readystatechange', function () {
        if (xhr.readyState === 4) {
          if (xhr.status >= 200 && xhr.status < 300) {
            // 手动对数据转化
            // let data = JSON.parse(xhr.response)
            // console.log(data) // {name: 'yuanxinyuehahaha'}
            // result.innerHTML = data.name

            // 自动转化
            console.log(xhr.response) // {name: 'yuanxinyuehahaha'}
            result.innerHTML = xhr.response.name
          }
        }
      })
    })
  </script>
</body>

</html>
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

server.js

// 可以接收任意类型的请求
app.all('/json-server', (request, response) => {
  response.setHeader('Access-Control-Allow-Origin', '*')
  // 任何类型的请求头都可以接受(自定义的也行)
  response.setHeader('Access-Control-Allow-Headers', '*')
  // 响应一个数据
  const data = {
    name: 'yuanxinyuehahaha'
  }
  // 对对象进行字符串转换
  let str = JSON.stringify(data)
  // send 只支持字符串和 buffer 类型(专门存储二进制数据的)
  response.send(str)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14

2.8 nodemon 自动重启工具

前面的操作中,只要修改了后端代码,就需要重新运行一遍:node server.js,特别麻烦

  1. 安装:yarn add nodemon
  2. 终端输入:npx nodemon server.js,就能开启服务了

2.9 请求超时与网络异常处理

net.html

const xhr = new XMLHttpRequest()
// 超时设置 2s 设置
xhr.timeout = 2000
// 超时回调
xhr.addEventListener('timeout', function () {
  alert('网络异常,请稍后重试')
})
// 网络异常回调
xhr.addEventListener('error', function () {
  alert('你的网络似乎除了一些问题')
})
xhr.open('GET', 'http://127.0.0.1:8000/delay')
1
2
3
4
5
6
7
8
9
10
11
12

server.js

// 延时响应
app.all('/delay', (request, response) => {
  response.setHeader('Access-Control-Allow-Origin', '*')
  setTimeout(() => {
    response.send('延时响应')
  }, 3000);
})
1
2
3
4
5
6
7

2.10 取消请求

cancel.html

// 获取元素对象
const btns = document.querySelectorAll('button')
let x = null

btns[0].addEventListener('click', function () {
  x = new XMLHttpRequest()
  x.open('GET', 'http://127.0.0.1:8000/delay')
  x.send()
})

// abort
btns[1].addEventListener('click', function () {
  x.abort()
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14

server.js

app.all('/delay', (request, response) => {
  response.setHeader('Access-Control-Allow-Origin', '*')
  setTimeout(() => {
    response.send('延时响应')
  }, 3000);
})
1
2
3
4
5
6

2.11 请求重复发送问题

如果有相同的请求,则取消当前发送的重复请求

repeat.html

<body>
  <button>点击发送</button>
  <script>
    // 获取元素对象
    const btns = document.querySelectorAll('button')
    let x = null
    // 表示变量
    let isSending = false // 是否正在发送 ajax 请求


    btns[0].addEventListener('click', function () {
      // 判断表示变量
      if (isSending) x.abort() // 如果正在发送,则取消该请求,创建一个新的请求
      x = new XMLHttpRequest()
      isSending = true
      x.open('GET', 'http://127.0.0.1:8000/delay')
      x.send()
      x.addEventListener('readystatechange', function () {
        if (x.readyState === 4) {
          // 修改标识变量
          isSending = false
        }
      })
    })
  </script>
</body>
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

server.js

app.all('/delay', (request, response) => {
  response.setHeader('Access-Control-Allow-Origin', '*')
  setTimeout(() => {
    response.send('延时响应')
  }, 3000);
})
1
2
3
4
5
6

3. jQuery 的 ajax

3.1 $.get、$.post

jquery.html

<h2>jQuery发送Ajax请求</h2>
<button>get</button>
<button>post</button>
<button>通用性方法ajax</button>
<script>
  $('button').eq(0).on('click', function () {
    // 参数:请求行的 url、请求参数、响应体
    $.get('http://127.0.0.1:8000/jquery-server', {a: 100, b: 200}, (data) => {
      console.log(data) // {"name":"yuanke"}
    })
  })
  $('button').eq(1).on('click', function () {
    // 参数:请求行的 url、请求参数、响应体、响应体类型
    $.post('http://127.0.0.1:8000/jquery-server', {a: 100, b: 200}, (data) => {
      console.log(data) // {name: 'yuanke'}
    }, 'json')
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

server.js

// jquery 服务
app.all('/jquery-server', (request, response) => {
  response.setHeader('Access-Control-Allow-Origin', '*')
  const data = {name: 'yuanke'}
  response.send(JSON.stringify(data))
})
1
2
3
4
5
6

3.2 jQuery 通用方法发送 AJAX 请求

jquery.html

$('button').eq(2).on('click', function () {
  $.ajax({
    // url
    url: 'http://127.0.0.1:8000/jquery-server',
    // 请求参数 / 请求体,get / post 用这个
    data: {a: 100, b: 200},
    // 请求类型
    type: 'GET',
    // 响应体结果
    dataType: 'json',
    // 回调获得响应体
    success: data => {
      console.log(data) // {name: 'yuanke'}
    },
    timeout: 2000,
    // 失败的回调
    error: () => {
      console.log('出错了!')
    },
    // 自定义请求头
    headers: {
      c: 300,
      d: 400
    }
  })
})
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

server.js

// jquery 服务
app.all('/jquery-server', (request, response) => {
  response.setHeader('Access-Control-Allow-Headers', '*')
  response.setHeader('Access-Control-Allow-Origin', '*')
  const data = {name: 'yuanke'}
  response.send(JSON.stringify(data))
})
1
2
3
4
5
6
7

4. axios

4.1 基本形式

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>axios</title>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
  <button>GET</button>
  <button>POST</button>
  <button>AJAX</button>

  <script>
    const btns = document.querySelectorAll('button')

    btns[0].addEventListener('click', function () {
      // GET 请求
      axios.get('http://127.0.0.1:8000/axios-server', {
        // url 参数(get 里的请求参数)
        params: {
          id: 100,
          vip: 7
        },
        headers: {
          name: 'yuanxin',
          age: 20
        }
      })
    })
  </script>
</body>
</html>
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

4.2 axios.get / axios.post

axios/axios: Promise based HTTP client for the browser and node.js (github.com)open in new window

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>axios</title>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>

<body>
  <button>GET</button>
  <button>POST</button>
  <button>AJAX</button>

  <script>
    const btns = document.querySelectorAll('button')

    // 配置 baseURL
    axios.defaults.baseURL = 'http://127.0.0.1:8000'

    btns[0].addEventListener('click', function () {
      // GET 请求
      axios.get('/axios-server', {
        // url 参数(get 里的请求参数和 post 里的请求体)
        params: {
          id: 100,
          vip: 7
        },
        headers: {
          name: 'yuanxin',
          age: 20
        }
      }).then(value => {
        console.log(value)
      })
    })

    btns[1].addEventListener('click', function () {
      // POST 请求
      axios.post('/axios-server', {
        // post 的话,第二个参数是一个请求体
        username: 'admin',
        password: 'admin'
      }, {
        // 请求参数
        params: {
          id: 200,
          vip: 9
        },
        // 请求头
        headers: {
          height: 100,
          weight: 180
        }
      })
    })
  </script>
</body>

</html>
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
59
60
61
62

4.3 axios 通用方法

btns[2].addEventListener('click', function () {
  axios({
    // 请求方法
    method: 'POST',
    // url
    url: '/axios-server',
    // 请求参数
    params: {
      vip: 10,
      level: 30
    },
    // 请求头
    headers: {
      a: 100,
      b: 200
    },
    // 请求体
    data: {
      username: 'admin',
      password: 'admin'
    }
  }).then(response => {
    console.log(response)
    // 响应状态码
    console.log(response.status)
    // 响应状态字符串
    console.log(response.statusText)
    // 响应头
    console.log(response.headers)
    // 响应体
    console.log(response.data)
  })
})
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

5.fetch

fetch.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <button>AJAX请求</button>
  <script>
    const btn = document.querySelector('button')
    btn.addEventListener('click', function () {
      fetch('http://127.0.0.1:8000/fetch-server', {
        // 请求方法
        method: 'POST',
        // 请求头
        headers: {
          'name': 'yuanke'
        },
        // 请求体
        body: 'username=admin&password=admin'
      }).then(response => {
        // return response.text()
        return response.json()
      }).then(response => {
        console.log(response)
      })
    })
  </script>
</body>

</html>
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

server.js

// fetch 服务
app.all('/fetch-server', (request, response) => {
  response.setHeader('Access-Control-Allow-Headers', '*')
  response.setHeader('Access-Control-Allow-Origin', '*')
  const data = {name: 'yuanke'}
  response.send(JSON.stringify(data))
})
1
2
3
4
5
6
7

6.其他

6.1 同源策略

网页的 url 和目标资源的 utl 的协议、域名、端口号必须完全相同。违背同源策略就是跨域

同源策略小案例

只要输入 127.0.0.1:9000/home 就能进入该案例

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <h1>haha</h1>
  <button>点击获取用户数据</button>
  <script>
    const btn = document.querySelector('button')
    btn.addEventListener('click', function () {
      const x = new XMLHttpRequest()
      // 这里因为是满足同源策略的,所以 url 可以简写
      x.open('GET', '/data')
      // 发送
      x.send()
      x.addEventListener('readystatechange', function () {
        if (x.readyState === 4) {
          if (x.status >= 200 && x.status < 300) {
            console.log(x.response)
          }
        }
      })
    })
  </script>
</body>

</html>
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

server.js

const express = require('express')
const app = express()

app.get('/home', (request, response) => {
  // 响应一个页面
  response.sendFile(__dirname + '/index.html')
})

app.get('/data', (request, response) => {
  // 发送响应体
  response.send('用户数据')
})

app.listen(9000, () => {
  console.log('服务已经启动')
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

6.2 解决跨域 - JSONP

JSONP 是一个非官方的跨域解决方案,纯粹凭借程序员聪明才智开发出来的,只支持 get 请求

工作方法

网页一些标签天生具有跨域能力,例如 img、link、iframe、script,JSONP 就是利用 script 标签的跨域能力来发送请求的

使用方法

  1. 客户端使用 script 标签引入服务端 url
  2. 服务端需要返回 js 代码(response.end('console.log("hhh")')
  3. 如果服务端返回一个函数名(带参数),则客户端必须要声明该函数

示例代码

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>axios</title>
  <style>
    #result {
      width: 300px;
      height: 100px;
      border: solid 1px #78a;
    }
  </style>
</head>

<body>
  <div id="result"></div>
  <script>
    // 处理函数
    function handle(data) {
      // 获取 result 元素
      const result = document.querySelector('#result')
      result.innerHTML = data.name
    }
  </script>
  <script src="http://127.0.0.1:8000/jsonp-server"></script>
</body>

</html>
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

server.js

// jsonp 服务
app.all('/jsonp-server', (request, response) => {
  // response.send('console.log("hello jsonp")')
  const data = {
    name: '尚硅谷aa'
  }
  // 将数据转化成字符串
  let str = JSON.stringify(data)
  // 返回结果
  response.end(`handle(${str})`)
})
1
2
3
4
5
6
7
8
9
10
11

6.3 原生 jsonp 实践

文本框输入内容,点击文本框外的区域进行确认。这时文本框边框变红,且响应体的 msg 显示在下面

index.html

<body>
  用户名:<input type="text" id="username" />
  <p></p>
  <script>
    const input = document.querySelector('input')
    const p = document.querySelector('p')

    // 声明 handle 函数
    function handle(data) {
      input.style.border = 'solid 10px #f00'
      p.innerHTML = data.msg
    }

    input.addEventListener('blur', function () {
      let username = this.value
      // 向服务端发送请求,检测用户名是否存在
      // 1.创建一个 script 标签
      const script = document.createElement('script')
      // 2.设置标签的 src 属性
      script.src = 'http://127.0.0.1:8000/check-username'
      // 3.将 script 插入到文档中
      document.body.appendChild(script)
    })
  </script>
</body>
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

server.js

// 用户名检测是否存在
app.all('/check-username', (request, response) => {
  const data = {
    exist: 1,
    msg: '用户名已经存在'
  }
  // 将数据转化成字符串
  let str = JSON.stringify(data)
  // 返回结果
  response.end(`handle(${str})`)
})
1
2
3
4
5
6
7
8
9
10
11

6.4 CORS 跨域

Cross-Origin Resource Sharing:跨域资源共享

跨源资源共享(CORS) - HTTP | MDN (mozilla.org)open in new window

response.setHeader('Access-Control-Allow-Origin', '*') // 允许任何 url 跨域
response.setHeader('Access-Control-Allow-Headers', '*') // 允许自定义请求头
response.setHeader('Access-Control-Allow-Method', '*') // 允许任意请求方法
1
2
3

7.axios 深入了解

7.1 json-server 的安装

官方网站:typicode/json-server: Get a full fake REST API with zero coding in less than 30 seconds (seriously) (github.com)open in new window

  1. Install JSON Server: yarn global add json-server

  2. Create a db.json file with some data:

    {
      "posts": [
        { "id": 1, "title": "json-server", "author": "typicode" }
      ],
      "comments": [
        { "id": 1, "body": "some comment", "postId": 1 }
      ],
      "profile": { "name": "typicode" }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  3. Start JSON Server: json-server --watch db.json


7.2 json-server 基本使用

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
  <h1>基本使用</h1>
  <button>发送GET请求</button>
  <button>发送POST请求</button>
  <button>发送PUT请求</button>
  <button>发送DELETE请求</button>
  <script>
    const btns = document.querySelectorAll('button')
    btns[0].addEventListener('click', function () {
      // 发送 axios 请求
      axios({
        // 请求类型
        method: 'GET',
        url: 'http://localhost:3000/posts/2'
      }).then(response => {
        console.log(response)
      })
    })

    // 添加一篇新的文章,id 自动 +1
    btns[1].addEventListener('click', function () {
      axios({
        method: 'POST',
        // 向 db.json 的 posts数组 中添加请求体的内容
        url: 'http://localhost:3000/posts',
        // 设置请求体
        data: {
          title: '今天天气不错,还挺风和日丽的',
          author: '张三'
        }
      }).then(response => {
        console.log(response)
      })
    })

    // 更新数据,url 后的 3 为要更新的内容
    btns[2].addEventListener('click', function () {
      axios({
        method: 'PUT',
        // 向 db.json 的 posts 数组中的第三个内容改成 data 里的数据
        url: 'http://localhost:3000/posts/3',
        // 设置请求体
        data: {
          title: '今天天气不错,还挺风和日丽的',
          author: '李四'
        }
      }).then(response => {
        console.log(response)
      })
    })

    // 删除数据
    btns[3].addEventListener('click', function () {
      axios({
        method: 'DELETE',
        // 向 db.json 的 posts数组 中添加请求体的内容
        url: 'http://localhost:3000/posts/3'
      }).then(response => {
        console.log(response)
      })
    })
  </script>
</body>
</html>
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

7.3 axios 的基本使用

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>

<body>
  <h1>基本使用</h1>
  <button>发送GET请求</button>
  <button>发送POST请求</button>
  <button>发送PUT请求</button>
  <button>发送DELETE请求</button>
  <script>
    const btns = document.querySelectorAll('button')
    // 发送 GET 请求
    btns[0].addEventListener('click', function () {
      axios.request({
        method: 'GET',
        url: 'http://localhost:3000/comments'
      }).then(response => {
        console.log(response)
      })
    })

    // 发送 POST 请求
    btns[1].addEventListener('click', function () {
      axios.post('http://localhost:3000/comments', {
        'body': '喜大普奔',
        'postId': 2
      }).then(response => {
        console.log(response)
      })
    })
  </script>
</body>

</html>
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

7.4 axios 请求响应结果的结构

  1. config:包括请求行、请求头、请求体等
  2. data:响应体的结果,自动解析为了对象
  3. headers:响应头
  4. request:原生的 ajax 请求对象,例如就包括 status 响应状态码和 status 响应字符串等

7.5 axios 配置对象

axios/axios: Promise based HTTP client for the browser and node.js (github.com)open in new window

  • url、method、baseURL、params、headers:url、请求方法、基础url、请求参数、请求头
  • transformRequest: (data, headers) => {}:对请求体和请求头在发送前进行处理
  • transformResponse: data => {}:对响应体在 then 之前进行处理
  • paramsSerializer: params => {}:对请求参数进行序列化,转换成字符串
axios({
  url: '/post',
  params: {
    a: 100,
    b: 200
  }
  // 一般情况下,自动转化为 --> /post?a=100&b=200
  // 如果变成 --> /post/a/100/b/200
  // 或者 --> /post/a.100/b.200
})
1
2
3
4
5
6
7
8
9
10
  • data:请求体设置。两种形式:
    • 对象形式:自动将其转成 json 字符串形式传递
    • URL 参数形式:直接传递
  • timeout:超时时间
  • withCredentials:跨域请求时对 cookie 的携带做一个设置,false 为不携带,true 为在跨域请求时把 cookie 携带过去
  • adapter: config => {}:对请求的适配器做设置,有两种:一种是在客户端发送 ajax 的,一种是在 nodejs 环境发送 http 请求的
  • auth:对请求的基础验证(较少使用)
  • responseType:默认为 json,即服务器默认返回响应体格式为 json 格式
  • responseEncoding:响应结果的编码,默认为 utf-8
  • xsrfCookieName、xsrfHeaderName:对 cookie 的名字和 header 的名字做唯一标识,为了防止跨站攻击
  • onUploadProgress: progressEvent => {}:上传的回调
  • onDownloadProgress: progressEvent => {}:下载的回调
  • maxContentLength:http 响应体的最大内容,单位为字节
  • maxBodyLength:请求体的最大内容,单位为字节
  • validateStatus: status => {}:对响应结果的成功做设置(什么情况下认定成功)
  • maxRedirects:最大跳转次数
  • socketPath:设定 socket 文件的位置,向 docker 的守护进程做数据转发
  • httpAgent:少用
  • httpsAgent:少用
  • proxy:代理,特别常用
  • cancelToken:对 ajax 做一个取消的设置
  • decompress:压缩,nodejs 才能用

7.6 默认配置 config

形式:

axios.defaults.xxx = 'xxx'
1

示例代码:

<body>
  <h1>基本使用</h1>
  <button>发送GET请求</button>
  <button>发送POST请求</button>
  <button>发送PUT请求</button>
  <button>发送DELETE请求</button>
  <script>
    const btns = document.querySelectorAll('button')
    // 默认配置
    axios.defaults.method = 'GET' // 设置默认请求类型为 GET
    axios.defaults.baseURL = 'http://localhost:3000' // 设置基础 URL
    axios.defaults.params = { id: 100 }
    axios.defaults.timeout = 3000
    
    btns[0].addEventListener('click', function () {
      axios({
        url: '/posts'
      }).then(response => {
        console.log(response)
      })
    })
  </script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

7.7 创建实例对象发送请求

上面的默认配置在从多个服务器发送数据时就不好使了,这时候需要一个通用的配置:实例对象

示例代码:

<script>
  const btns = document.querySelectorAll('button')
  const duanzi = axios.create({
    baseURL: 'https://api.apiopen.top',
    timeout: 2000
  })

  // 这里 duanzi 与 axios 对象的功能几近是一样的
  duanzi({
    url: '/getJoke'
  }).then(response => {
    console.log(response)
  })

  duanzi.get('/getJoke').then(response => {
    console.log(response.data)
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

7.8 拦截器

  1. 拦截器执行顺序是:请求拦截器越后越先拦截,响应拦截器越先越先执行
  2. 请求拦截器可以拦截配置对象,并对配置对象进行操作(例如:config.params = { a: 200 }),然后返回 config
  3. 响应拦截器可以拦截 response,并对 response 进行操作(例如:return response.data
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>

<body>
  <script>
    // 执行顺序 2 1 1 2
    // 设置请求拦截器 config 配置对象
    axios.interceptors.request.use(config => {
      console.log('请求拦截器 成功 - 1号')
      // 修改 config 里的参数
      config.params = { a: 100 }
      return config
      // throw '参数除了问题'
    }, error => {
      console.log('请求拦截器 失败 - 1号')
      return Promise.reject(error)
    })

    axios.interceptors.request.use(config => {
      console.log('请求拦截器 成功 - 2号')
      // 修改 config 中参数
      config.timeout = 2000
      return config
      // throw '参数除了问题'
    }, error => {
      console.log('请求拦截器 失败 - 2号')
      return Promise.reject(error)
    })

    // 设置响应拦截器
    axios.interceptors.response.use(response => {
      console.log('响应拦截器 成功 - 1号')
      return response.data
    }, error => {
      console.log('响应拦截器 失败 - 1号')
      return Promise.reject(error)
    })

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

    // 发送请求
    axios({
      method: 'GET',
      url: 'http://localhost:3000/posts'
    }).then(response => {
      console.log('自定义回调处理成功的结果')
    }).catch(reason => {
      console.log('自定义失败回调', reason)
    }) 
  </script>
</body>

</html>
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
59
60
61
62
63
64
65
66
67
68

7.9 axios 取消请求

  1. 设置 json-server 的延时:npx json-server --watch db.json -d 2000
  2. 普通实例:
<body>
  <button>发送请求</button>
  <button>取消请求</button>
  <script>
    const btns = document.querySelectorAll('button')
    // 2.声明全局变量
    let cancel = null
    btns[0].addEventListener('click', function () {
      axios({
        method: 'GET',
        url: 'http://localhost:3000/posts',
        // 1.添加配置对象的属性
        cancelToken: new axios.CancelToken(c => {
          // 3.将 c 的值赋值给 cancel
          cancel = c
        })
      }).then(response => {
        console.log(response)
      })
    })

    btns[1].addEventListener('click', function () {
      cancel()
    })
  </script>
</body>
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
  1. 用户发送多个请求,取消重复的还在进行的请求:
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>

<body>
  <button>发送请求</button>
  <button>取消请求</button>
  <script>
    const btns = document.querySelectorAll('button')
    // 2.声明全局变量
    let cancel = null
    btns[0].addEventListener('click', function () {
      // 检测上一次请求是否已经完成
      if (cancel !== null) {
        cancel()
      }
      axios({
        method: 'GET',
        url: 'http://localhost:3000/posts',
        // 1.添加配置对象的属性
        cancelToken: new axios.CancelToken(c => {
          // 3.将 c 的值赋值给 cancel
          cancel = c
        })
      }).then(response => {
        console.log(response)
        // 将 cancel 值初始化设置
        cancel = null
      })
    })

    btns[1].addEventListener('click', function () {
      cancel()
    })
  </script>
</body>

</html>
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