Note | Ajax

前言:此为笔者大学时期记的笔记,可能有地方写的不够准确,见谅。

Day1

原生AJAX

AJAX 简介

AJAX 全称为 Asynchronous JavaScript And XML,就是异步的 JS 和 XML。

通过 AJAX 可以在浏览器中向服务器发送异步请求,最大的优势:无刷新获取数据

AJAX 不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式。

XML 简介

XML 可扩展标记语言。

XML 被设计用来传输和存储数据。

XML 和 HTML 类似,不同的是 HTML 中都是预定义标签,而 XML 中没有预定义标签,全都是自定义标签,用来表示一些数据。

image-20210818224439409

现在已经被 JSON 取代了,灵活度远胜于XML。

image-20210818224511926

AJAX 的特点

AJAX 的优点

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

AJAX 的缺点

  1. 没有浏览历史,不能回退
  2. 存在跨域问题(同源)
  3. SEO 不友好(SEO——>搜索引擎优化Search Engine Optimization)——>网页中内容爬取不到

HTTP

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

请求报文(浏览器向客户端发送请求)

重点是格式与参数

行      POST(请求类型)  /s?ie=utf-8(url路径)  HTTP/1.1 (http协议版本)
头      Host: atguigu.com
        Cookie: name=guigu
        Content-type: application/x-www-form-urlencoded(告知服务器请求类型)
        User-Agent: chrome 83
空行
体      username=admin&password=admin

头的格式基本上是 名字: 值

如果类型为get请求,请求体为空,如果是post请求,请求体可以不为空

响应报文(客户端向浏览器返回结果)

行      HTTP/1.1(协议版本)  200(响应状态码)  OK(响应状态字符串)
头      Content-Type: text/html;charset=utf-8(类型)
        Content-length: 2048(长度)
        Content-encoding: gzip(压缩方式)
空行    
体      <html>
            <head>
            </head>
            <body>
                <h1>尚硅谷</h1>
            </body>
        </html>

常见状态码

  • 404
  • 403
  • 401
  • 500
  • 200

Day2

Chrome网络控制台查看通信报文

下为GET请求

打开Chrome浏览器,打开百度随机输入一个搜索关键词点击搜索,按下F12,切换到Network,然后点击第一个选项

image-20210819150849916

现在我们重点关注Header和Response选项卡,Preview相当于一个预览,对响应体解析之后的结果的预览

在Header页面有以下4项,我们重点关注中间这两个

image-20210819151233963

Response Header是响应头,Request Header是请求头

Request Header

我们这里先看请求头,这样我们就可以知道,刷新页面的时候浏览器到底向服务器发送的内容

点开我们就可以看到这里都是请求头的格式都是名字:值,没有请求行

image-20210819151630073

点击选项卡旁边的view source

image-20210819151733816

image-20210819151742830

可见最上面的一行已经变成请求行的格式了,关于请求体一会补充,接下来先我们看下四个选项中的Query String Parameters

Query String Parameters

image-20210819152039936

这个是对我们url上的参数做解析,相当于对参数做了一定程度的格式化

image-20210819152101895

Response Header

直接点开我们可以发现都是响应头信息,依旧都是名字: 值的格式

image-20210819152332399

点击view source我们就可以看到包含请求行的格式了

image-20210819152516118

如果要看响应体(服务端返回的html内容),我们点击下图的选项卡

image-20210819152834154

下为POST请求

比如我们登录的时候就有发送POST请求

image-20210819153808974

Request Header

image-20210819153844020

上图方法变为了 POST 路径为 /login 以及协议版本为 HTTP/1.1

现在我们看下请求体,点击Headers下的Form Data选项卡

image-20210819154609782

点击view source我们就可以看到原始的请求体信息了

image-20210819154757182

这里就是我们点击提交表单之后,浏览器会把http的报文封装好,封装好之后发送到目标服务器的指定端口做请求

Response Header

这块的响应体因为是跳转页面所以这里的响应体是空的

image-20210819155301399

image-20210819155308209

Node.js安装

express框架介绍与基本使用

【点击这里进入官网】

介绍

基于 Node.js 平台,快速、开放、极简的 Web 开发框架

npm创建模块

创建模块,package.json 文件是必不可少的。我们可以使用 NPM 生成 package.json 文件,生成的文件包含了基本的结果,我们在文件夹下输入命令

1
npm init --yes

在最后输入 "yes" 后会生成 package.json 文件。

安装

1
$ npm install express --save

基本使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// 1.引入express
// import express from '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 端口监听中。。。")
})

然后我们输入命令node 文件名.js,就可以看到控制台输入如下语句

image-20210819162129762

打开浏览器输入网址http://127.0.0.1:8000/

image-20210819162553005

现在我们就已经把服务启动了,这是一个http的一个服务,现在我们就可以借助于这个服务和前端的ajax做交互

AJAX案例准备

现在我们创建两个文件GET.htmlserver.js,前者是作为前端页面的,后者是作为我们的服务端。

前端页面准备

GET.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<!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>AJA GET 请求</title>
    <style>
        #result{
            width: 200px;
            height: 100px;
            border: solid 1px #90b;
        }
    </style>
</head>
<body>
    <button>点击发送请求</button>
    <div id="result"></div>
</body>
</html>

image-20210819164640498

需求:点击按钮之后向服务端发送请求,然后把服务端返回的响应体结果在div中做一个呈现

服务准备

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// 1.引入express
// import express from '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 端口监听中。。。")
})

控制台启动服务

image-20210819171350927

浏览器输入http://127.0.0.1:8000/server

image-20210819171447249

这里可以看到我们跨域设置成功了

image-20210819171544324

至此我们准备工作已经完成了

Day3

AJAX请求的基本操作

准备工作完成后我们就可以写些基本的代码了,下面我们开始吧

基本4步

AJAX操作分为4个步骤

1.创建对象

1
const xhr = new XMLHttpRequest();

在网络控制这里也有一个xhr,点击会对Ajax请求做一个筛选,所以我们以后看到xhr后需要想到是Ajax

image-20210820155636737

2.初始化

设置请求方法和 url

1
xhr.open('GET','http://127.0.0.1:8000/server');

目前这个阶段url需要写全

3.发送

1
xhr.send();

4.事件绑定

处理服务端返回的结果

1
2
3
xhr.onreadystatechange = function () {

}

我们对这个方法的方法名做一个解析

on——when 当....时候

readystate 是 xhr 对象中的属性,表示状态有0、 1、 2、3、4

  • 0代表未初始化,readystate最开始的值就是0
  • 1代表open方法调用完毕
  • 2代表send方法调用完毕
  • 3代表服务端返回了部分结果
  • 4代表服务端返回了所有结果

所以我们可以根据上面的(readystate)状态来判断是否需要对结果进行处理,正常情况下我们希望返回结果为4的时候,也就是所有结果都出来的时候对代码进行处理,然后我们需要判断响应的状态码(status)为2XX的时候将返回的响应体嵌入div标签中即可

按照要求我们把代码写出来

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
xhr.onreadystatechange = function () {
                // 判断
                if(xhr.readyState === 4){
                    // 判断响应状态码 200 404 403 401 500
                    // 2XX 都代表成功
                    if (xhr.status >= 200 && xhr.status < 300) {
                        // 处理结果 行 头 空行 体
                        // 响应
                        // console.log(xhr.status); // 状态码
                        // console.log(xhr.statusText); // 状态字符串
                        // console.log(xhr.getAllResponseHeaders()); // 所有响应头
                        // console.log(xhr.response);// 响应体
                        result.innerHTML = xhr.response
                     }else {

                    }
     		}
     }
}

补充各项处理结果的获取方式

  • 状态码获取方式:xhr对象.status
  • 状态字符串获取方式:xhr对象.statusText
  • 所有响应头获取方式:xhr对象.getAllResponseHeaders()
  • 响应体获取方式:xhr对象.response

完整代码

GET.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
<!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 GET 请求</title>
    <style>
        #result{
            width: 200px;
            height: 100px;
            border: solid 1px #90b;
        }
    </style>
</head>
<body>
    <button>点击发送请求</button>
    <div id="result"></div>
    <script>
        //获取button元素
        const btn = document.getElementsByTagName('button')[0];
        const result = document.getElementById('result');
        //绑定事件
        btn.onclick = function () {
            //1. 创建对象
            const xhr = new XMLHttpRequest();
            //2. 初始化 设置请求方法和 url
            xhr.open('GET','http://127.0.0.1:8000/server');
            //3. 发送
            xhr.send();
            //4. 事件绑定 处理服务端返回的结果
            // on when 当....时候
            // readystate 是 xhr 对象中的属性,表示状态 0() 1 2 3 4
            // change 改变的时候触发
            xhr.onreadystatechange = function () {
                // 判断
                if(xhr.readyState === 4){
                    // 判断响应状态码 200 404 403 401 500
                    // 2XX 都代表成功
                    if (xhr.status >= 200 && xhr.status < 300) {
                        // 处理结果 行 头 空行 体
                        // 响应
                        // console.log(xhr.status); // 状态码
                        // console.log(xhr.statusText); // 状态字符串
                        // console.log(xhr.getAllResponseHeaders()); // 所有响应头
                        // console.log(xhr.response);// 响应体
                        result.innerHTML = xhr.response
                     }else {

                    }
                }
            }
        }
    </script>
</body>
</html>

server.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// 1.引入express
// import express from '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 端口监听中。。。")
})

AJAX设置请求参数

我们直接在刚才发送请求的url后面加上参数

image-20210820211357686

点击按钮再次发送请求,在Header中我们可以查看当前的Query String

image-20210820211421899

AJAX发送POST请求

image-20210820212810054

当我们把鼠标放在框内的时候发送POST请求,返回来以后把响应体结果放在框内做一个呈现

大部分代码与之前相同,但是事件监听的方式不同,请求类型也不同

POST.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
<!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 POST 请求</title>
    <style>
        #result {
            width: 200px;
            height: 100px;
            border: solid 1px #903;
        }
    </style>
</head>
<body>
    <div id="result"></div>
    <script>
        // 获取元素对象
        const result = document.getElementById("result");
        // 绑定事件
        result.addEventListener('mouseover',function(){
            // 1. 创建对象
            const xhr = new XMLHttpRequest();
            // 2. 初始化 设置类型与 URL
            xhr.open('POST','http://127.0.0.1:8000/server');
            // 3. 发送
            xhr.send();
            // 4.事件绑定
            xhr.onreadystatechange = function () {
                // 判断
                if (xhr.readyState === 4) {
                    if (xhr.status >= 200 && xhr.status < 300) {
                        // 处理服务端返回的结果
                        result.innerHTML = xhr.response;
                    }
                }
            }
        });
    </script>
</body>
</html>

server.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
// 1.引入express
// import express from '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 端口监听中。。。")
})

**注意:**这里修改server.js需要重启服务才能生效

AJAX-POST设置请求体

请求体是在send方法中设置的,我们可以设置任意格式的数据

image-20210820215820253

在NetWork控制台中查看

image-20210820215911470

AJAX-设置请求头信息

只需要在open方法的后面做一个新的方法的调用就可以了

image-20210820225141686

**注:**Content-Type设置请求体内容的类型,后面的是参数查询字符串的类型(注意是固定写法)

自定义头信息(了解)

我们也可以自定义头信息

image-20210820225218119

这里还需要在server.js里面设置下特殊的响应头,实际工作中后端的人员会帮忙完成

image-20210820225433917

因为这里不能直接使用post,可能校验的时候会用到其他方法所以改用all

总结

一般来说我们会把身份校验的信息放在头信息里面,然后把它传给服务器,服务器对参数进行提取,对用户身份进行校验

Day4

AJAX请求服务端响应json数据

实际工作中返回的大多数情况都是json格式的数据

这里我们在服务端定义一个json格式的数据,这里我们记得需要使用JSON.stringify(data)方法数据进行字符串的转换

image-20210821115841962

然后我们在牵头可以有两种方式对数据进行转换后再输出数据

1.手动对数据转换

 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
<!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 #89b;
        }
    </style>
</head>
<body>
    <div id="result">

    </div>
    <script>
        const result = document.getElementById('result');
        //绑定键盘按下事件
        window.onkeydown = function () {
            //发送请求
            const xhr = new XMLHttpRequest();
            // 初始化
            xhr.open('GET','http://127.0.0.1:8000/json-server');
            //发送
            xhr.send();
            // 事件绑定
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                    if (xhr.status >= 200 && xhr.status < 300) {

                        // 1.手动对数据转换
                        let data = JSON.parse(xhr.response);
                        console.log(data);
                        result.innerHTML = data.name;
                    }
                }
            } 
        }
    </script>
</body>
</html>

转换需要手动调用JSON.parse(xhr.response)方法把数据转换为json格式

2.自动转换

这里我们需要先设置响应体数据的类型,通过xhr.responsseType = 'json'把数据类型设置为json类型

设置完之后返回回来的xhr.response就会自动转换为json类型了

 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
<!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 #89b;
        }
    </style>
</head>
<body>
    <div id="result">

    </div>
    <script>
        const result = document.getElementById('result');
        //绑定键盘按下事件
        window.onkeydown = function () {
            //发送请求
            const xhr = new XMLHttpRequest();
            // 设置响应体数据的类型
            xhr.responseType = 'json';
            // 初始化
            xhr.open('GET','http://127.0.0.1:8000/json-server');
            //发送
            xhr.send();
            // 事件绑定
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                    if (xhr.status >= 200 && xhr.status < 300) {
                        // 2.自动转换
                        console.log(xhr.response);

                        result.innerHTML = xhr.response.name;
                    }
                }
            } 
        }
    </script>
</body>
</html>

nodemon自动重启工具

之前一修改server.js就会导致我们需要重启才能让代码生效,nodemon这个工具就可以帮助我们解决这个问题

安装

npm install -g nodemon

重启服务

这时候我们就可以抛弃掉node XXX.js这个指令了,改用nodemon xxx.js这个指令,我们有修改js文件自动重启服务

AJAX中IE缓存问题

IE浏览器会对Ajax的结果进行一个缓存,导致下一次再次发送请求时走的是本地的缓存而不是服务器的最新数据

解决办法

对于这个问题我们的解决办法是每次发请求的url后面加上一个参数,保证这个参数每次都不一样,这样浏览器就会认为每次都是不同的请求,这时候就会发送一个新的请求而不是走本地缓存

image-20210821162607872

代码

IE.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
<!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 #258;
        }
    </style>
</head>
<body>
    <button>点击发送请求</button>
    <div id="result">

    </div>
    <script>
        const btn = document.getElementsByTagName('button')[0];
        const result = document.getElementById('result');
        btn.addEventListener('click',function () {
            const xhr = new XMLHttpRequest();
            xhr.open('GET','http://127.0.0.1:8000/ie?t='+Date.now());
            xhr.send();
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                    if (xhr.status >= 200 && xhr.status < 300) {
                        result.innerHTML = xhr.response;
                    }
                }
            }
        })
    </script>
</body>
</html>

server.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
// 1.引入express
// import express from '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.all('/server', (request, response)=>{
    //设置响应头 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*')
    // 响应头
    response.setHeader('Access-Control-Allow-Headers','*')
    //设置响应
    response.send('HELLO AJAX POST-2')
})

app.all('/json-server', (request, response)=>{
    //设置响应头 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*')
    // 响应头
    response.setHeader('Access-Control-Allow-Headers','*')
    // 响应一个数据
    const data = {
        name:'wocbushiba'
    }
    //  进行字符串的转换
    let str = JSON.stringify(data);
    // 设置响应
    response.send(str)
})
// 针对ie缓存的规则
app.all('/ie', (request, response)=>{
    //设置响应头 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*')
    //设置响应
    response.send('HELLO IE - 4')
})
//4.监听端口启动服务
app.listen(8000, ()=>{
    console.log("服务以及启动,8000 端口监听中。。。")
})

AJAX请求超时与网络异常

思路

1.服务端设置延时的回调函数

首先要实现超时我们可以在服务端加上一个延时用的函数——>setTimeout

2.前端设置超时时间与对应的回调函数

然后就是在前端页面的js中我们需要设置延时的具体时长,通过xhr.timeout设置,然后我们需要设置超时后的回调函数,通过xhr.ontimeout = function () {}的方式

3.前端设置异常回调函数

最后关于网络异常我们只需设置一个异常回调函数即可,通过xhr.onerror = function () {}的方式

完整代码

异常.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
<!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 #90b;
        }
    </style>
</head>
<body>
    <button>点击发送请求</button>
    <div id="result">

    </div>
    <script>
        const btn = document.getElementsByTagName('button')[0];
        const result = document.getElementById('result');
        btn.addEventListener('click',function () {
            const xhr = new XMLHttpRequest();
            // 超时设置 2s 设置
            xhr.timeout = 2000;
            // 超时回调
            xhr.ontimeout = function () {
                // 实际开发中会使用div或者是遮罩层体验会好点
                alert("网络异常,请稍后重试");
            }
            // 网络异常问题
            xhr.onerror = function () {
                alert("你的网络似乎出现了异常");
            }
            xhr.open('GET','http://127.0.0.1:8000/delay');
            xhr.send();
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                    if (xhr.status >= 200 && xhr.status < 300) {
                        result.innerHTML = xhr.response;
                    }
                }
            }
        })
    </script>
</body>
</html>

server.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 延时响应
app.get('/delay', (request, response)=>{
    //设置响应头 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*')
    setTimeout(()=> {
        //设置响应
        response.send('延时响应')
    },3000)
    
})

AJAX取消请求

对ajax对象调用abort方法就可以取消请求

 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
<!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>点击发送</button>
    <button>点击取消</button>
    <script>
        // 获取元素对象
        const btns = document.querySelectorAll('button');
        const x = new XMLHttpRequest();
        btns[0].onclick = function () {
            
            x.open('GET','http://127.0.0.1:8000/delay');
            x.send();
            // x.onreadystatechange = 
        }
        // abort
        btns[1].onclick =function () {
            x.abort();
        }
    </script>
</body>
</html>

AJAX重复发送请求问题

问题来源

如果服务器响应比较慢,用户疯狂去点击,导致服务器压力非常大,接收到非常多的请求且为相同请求

解决思路

点击按钮后看看之前有没有相同请求,如果有就把请求取消掉然后发送一个新的请求,这样以来请求始终就只有一个,服务器压力也会减小

具体思路

这里我们需要创建一个标识变量isSending,通过这个标识变量我们来对发送的状态进行标记,如果正在发送调用abort方法然后创建一个新的请求

详情代码

重复请求问题.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
<!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>重复请求问题</title>
</head>
<body>
    <button>点击发送</button>
    <button>点击取消</button>
    <script>
        // 获取元素对象
        const btns = document.querySelectorAll('button');
        let x = null;
        // 标识变量
        let isSending = false; //是否正在发送ajax请求

        btns[0].onclick = function () {
            // 判断标识变量
            if (isSending) {
                x.abort(); //如果正在发送,则取消该请求,创建一个新的请求
            }
            x = new XMLHttpRequest();
            // 修改标识变量的值
            isSending = true;//正在发送
            x.open('GET','http://127.0.0.1:8000/delay');
            x.send();
            x.onreadystatechange = function () {
                if (x.readyState === 4) {
                    // 修改标识变量
                    isSending = false;//发送成功后修改为false
                }
            }
        }
        // abort
        btns[1].onclick =function () {
            x.abort();
        }
    </script>
</body>
</html>

jQuery发送Ajax请求

老生常谈了

get请求

1
2
3
4
5
$.get(url, [data], [callback], [type])
url:请求的 URL 地址
data:请求携带的参数
callback:载入成功时回调函数
type:设置返回内容格式xml, html, script, json, text, _default

image-20210821215735024

post请求

1
2
3
4
5
$.post(url, [data], [callback], [type])
url:请求的 URL 地址
data:请求携带的参数
callback:载入成功时回调函数
type:设置返回内容格式xml, html, script, json, text, _default

image-20210821220002393

具体代码

client.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
<!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>jQuery 发送 AJAX 请求</title>
    <link crossorigin="anonymous" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <script crossorigin="anonymous" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script crossorigin="anonymous" src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>

<body>
    <div class="container">
        <h2 class="page-header">jQuery发送AJAX请求</h2>
        <button class="btn btn-primary">GET</button>
        <button class="btn btn-danger">POST</button>
        <button class="btn btn-info">通用型方法ajax</button>
    </div>
    <script>
        $('button').eq(0).click(function(){
            $.get('http://127.0.0.1:8000/jquery-server', {a:100,b:200}, function(data){
                console.log(data);
            },'json');
        })
        $('button').eq(1).click(function(){
            $.post('http://127.0.0.1:8000/jquery-server', {a:100,b:200}, function(data){
                console.log(data);
            },'json');
        })
    </script>

</body>

</html>

server.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// jQuery响应
app.all('/jquery-server', (request, response)=>{
    //设置响应头 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*')
    const data = {
        name:"sima"
    }
    //设置响应
    response.send(JSON.stringify(data))
    
})

jQuery通用方法发送Ajax请求

语法

 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
        $('button').eq(2).click(function(){
            $.ajax({
                // url
                url: 'http://127.0.0.1:8000/jquery-server',
                // 参数
                data: {
                    a: 100,
                    b: 200
                },
                // 请求类型
                type: 'GET',
                // 响应体结果
                dataType: 'json',
                // 成功的回调
                success: function(res){
                    console.log(res);
                },
                // 超时时间
                timeout: 2000,
                // 失败的回调
                error: function() {
                    console.log('出错啦@!');
                }
            });//参数是个对象
        })

其他的一些可选参数看【这里】

Axios发送Ajax请求

设置baseURL

通过axios.defaults.baseURL来设置baseURL,这样发送请求的时候就不要写完整路径了

get请求

axios#get(url[, config])

1
2
3
4
axios.get('/user?ID=12345')
  .then(function (response) {
    console.log(response);
  })

也可以这样写

1
2
3
4
5
6
7
8
axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(function (response) {
    console.log(response);
  })

还可以设置请求头信息

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
btns[0].onclick = function () {
            // GET 请求
            axios.get('/axios-server', {
                // url 参数
                params: {
                    id: 100,
                    vip: 7
                },
                // 请求头信息
                headers: {
                    name: 'bushiba',
                    age: 20
                }
            }).then(value => {
                console.log(value);
            })
        }

then方法返回的res相对比较全面

image-20210821232530976

post请求

axios.post(url[, data[, config]])

第二个参数是请求体,第三个才是配置对象,所以post中url参数和请求体可以同时设置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
axios.post('/axios-server', {
                username: 'admin',
                password: 'admin'
            }, {//post中url参数和请求体可以同时设置
                // url
                params: {
                    id: 200,
                    vip: 9
                },
                // 请求体参数
                headers: {
                    height: 180,
                    weight: 180
                },
            })

Day5

Axios函数发送Ajax请求

使用axios通用方法发送

axios(config)

1
2
3
4
5
6
7
8
9
// 发送 POST 请求
axios({
  method: 'post',
  url: '/user/12345',
  data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
  }
});
1
2
3
4
5
6
7
8
9
// 获取远端图片
axios({
  method:'get',
  url:'http://bit.ly/2mTM3nY',
  responseType:'stream'
})
  .then(function(response) {
  response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
});

如果不屑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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
        btns[1].onclick = function () {
            // POST 请求
            axios.post('/axios-server', {
                username: 'admin',
                password: 'admin'
            }, {//post中url参数和请求体可以同时设置
                // url
                params: {
                    id: 200,
                    vip: 9
                },
                // 请求体参数
                headers: {
                    height: 180,
                    weight: 180
                },
            })
        }

        btns[2].onclick = function () {
            axios({
                //请求方法
                method:'post',
                // url参数
                url: '/axios-server',
                // url参数
                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);
            })
        }

使用fetch函数发送Ajax请求

fetch函数是属于全局对象的下的一个方法,这个方法也可以发送Ajax请求

接受的参数参考下图

image-20210822155024682

测试代码

 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
<!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>fetch 发送 AJAX请求</title>
</head>
<body>
    <button>AJAX请求</button>
    <script>
        const btn = document.querySelector('button');

        btn.onclick = function () {
            fetch('http://127.0.0.1:8000/fetch-server?vip=10',{
                // 请求方法
                method: 'POST',
                // 请求头
                headers: {
                    name:'wocbushiba'
                },
                // 请求体
                body: 'username=admin&password=admin'
            }).then(response => {
                // console.log(response);
                // return response.text();
                return response.json();
            }).then(response => {
                console.log(response);
            })
        }
    </script>
</body>
</html>

这个方法的params是拼接在url后面的,而且需要获取response返回的数据需要调用response方法下的text()或者json(),text方法直接获取字符串,json方法可以把其转为json格式

这讲还涉及到promise相关的知识,有兴趣可以自己了解

fetch函数文档参考:链接

跨域

同源策略

同源策略(Same-Origin Policy)最早由 Netscape 公司提出,是浏览器的一种安全策略

同源: 协议、域名、端口号 必须完全相同。

违背同源策略就是跨域

Ajax是默认遵循同源策略的

接下来我们来模拟同源发送ajax请求

案例

首先我们写一个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
<!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>首页</title>
</head>
<body>
    <h1>民华大鼬</h1>
    <button>点击获取用户数据</button>
    <script>
        const btn = document.querySelector('button');
        btn.onclick = function () {
            const x = new XMLHttpRequest();
            // 满足同源策略,所以URL可以简写
            x.open('GET','/data');
            // 发送
            x.send();
            // 
            x.onreadystatechange = 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
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('服务以及启动,9000');
})

上面的home路径就是返回了刚才写的index页面,然后index页面上发送的请求也是来源于这个server的,这个时候就满足了同源的策略

如何解决跨域

JSONP

JSONP 是什么

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

JSONP 怎么工作的?

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

**注:**请求发送过去后,需要的是一个函数调用的内容,不然js引擎无法解析你的内容

JSONP实现跨域的原理

1.总结起来就是服务端返回的结果的形式就是一个函数的调用

2.函数的参数就是我们想给客户端返回的数据

3.这个函数前端必须提前声明,没有这个函数就会报错,这样前端函数就会对数据进行处理

image-20210822175036581

原生JSONP实践

image-20210822210019678

案例效果:输入用户名当失去焦点的时候下方出现关键字用户名已经存在(没有真的对用户名数据做校验只是通过模拟的数据来实现这个场景的功能)

前台代码

 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
<!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>案例</title>
</head>
<body>
    用户名<input type="text" id="username">
    <p></p>
    <script>
        // 获取input元素
        const input = document.querySelector('input');
        const p = document.querySelector('p');
        // 声明 handle 函数
        function handle (data) {
            input.style.border = "solid 1px #f00";
            // 修改 p 标签的提示文本
            p.innerHTML = data.msg;
        }

        // 绑定事件
        input.onblur = 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>
</html>

server.js——>对应部分的代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 用户名检测是否存在 
app.all('/check-username',(request,response)=>{
    // response.send('console.log("hello jsonp!")')
    const data = {
        exist: 1,
        msg: '用户名已经存在'
    };
    // 转化为字符串
    let str = JSON.stringify(data);
    response.end(`handle(${str})`);//end不会加特殊响应头
})

基本原理和上面一致

jQuery发送JSONP

要使用jQuery发送jsonp需要调用$.getJSON()

image-20210822213737739

jQuery内置了一个可以处理json数据的函数,只要在服务端调用该函数就行了,不用再特地去写一个“handle”函数了

要调用这个函数我们还需要再第一个参数(url)的后面加上?callback=?,这是个固定写法

image-20210822214034254

虽然我们写的是个问号,但是在实际发送请求时,这个参数是有值的,而服务端是可以把这个值接收到的,接收到之后需要把这个参数的值作为一个函数(jQuery注册了这样一个函数,函数名就是callback的值)

image-20210822214716156

服务端对应的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
app.all('/jquery-jsonp-server',(request,response)=>{
    // response.send('console.log("hello jsonp!")')
    const data = {
        name:'wocbushiba',
        city:['下北泽','民华大鼬','田所']
    };
    // 转化为字符串
    let str = JSON.stringify(data);
    // 接受callback这个参数
    let cb = request.query.callback;
    
    // 返回结果
    response.end(`${cb}(${str})`);//end不会加特殊响应头
})

然后我们前台就可以对返回的数据进行处理了!

前台对应的代码:

1
2
3
4
5
6
7
$.getJSON('http://127.0.0.1:8000/jquery-jsonp-server?callback=?',function(data){
                $('#result').html(`
                    名称:${data.name}
                    </p>
                    城市:${data.city}
                `)
})
参考代码

前台

 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
<!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>jQuery-jsonp</title>
    <style>
        #result{
            width: 300px;
            height: 100px;
            border: solid 1px #089;
        }
    </style>
    <script crossorigin="anonymous" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
    <button>点击发送jsonp请求</button>
    <div id="result">

    </div>
    <script>
        $('button').eq(0).click(function(){
            $.getJSON('http://127.0.0.1:8000/jquery-jsonp-server?callback=?',function(data){
                $('#result').html(`
                    名称:${data.name}
                    </p>
                    城市:${data.city}
                `)
            })
        })
    </script>
</body>
</html>

后台对应部分

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// jquery发送jsonp
app.all('/jquery-jsonp-server',(request,response)=>{
    // response.send('console.log("hello jsonp!")')
    const data = {
        name:'wocbushiba',
        city:['下北泽','民华大鼬','田所']
    };
    // 转化为字符串
    let str = JSON.stringify(data);
    // 接受callback这个参数
    let cb = request.query.callback;
    
    // 返回结果
    response.end(`${cb}(${str})`);//end不会加特殊响应头
})

CORS

【参考文档】

CORS 是什么?

CORS(Cross-Origin Resource Sharing),跨域资源共享。CORS 是**官方**的跨域解决方案,它的特点不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持get 和 post 请求。跨域资源共享标准新增了一组HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。

CORS 怎么工作的?

CORS 是通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。

设置CORS响应头实现跨域

通过 response 来设置响应头,来允许跨域请求

1
2
3
4
5
6
7
8
9
// cors
app.all('/cors-server',(request,response)=>{
    //设置响应头 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*');
    response.setHeader('Access-Control-Allow-Headers','*')
    response.setHeader('Access-Control-Allow-Methods','*')
    // response.setHeader("Access-Control-Allow-Origin","http://127.0.0.1:8000");
    response.send('hello CORS');
})

其他响应头可以看下面的链接

【http_响应首部字段】