简介:Ajax即Asynchronous Javascript And XML在 2005年被Jesse James Garrett提出的新术语…

第一章 HTTP协议

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

请求报文

从上到下为:

  • 请求行:你的url + 协议版本
  • 请求头:要访问的域名
  • 空行
  • 请求体:表单数据等等
1
2
3
4
5
6
7
GET /s?ie=utf-8&rsv_enter=1&rsv_dl=tb  HTTP/1.1
Host: www.baidu.com
Cookie: name=guigu
Content-type: application/x-www-form-urlencoded
User-Agent: chrome 83

username=admin&password=admin

响应报文

从上到下为:

  • 行:协议版本 + 响应状态码 + 响应字符串
  • 头:对响应体内容做一些描述
  • 空行
  • 体:具体html代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
HTTP/1.1  200  OK
Content-Type: text/html;charset=utf-8
Content-length: 2048
Content-encoding: gzip

<html>
<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>
<div id="root"></div>
</body>
</html>

第二章 原生Ajax

Ajax简介

  • Ajax:异步的js和XML

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

  • Ajax不是一门新的语言,而是一种将现有的标准组合在一起使用的新方式

XML简介

  • XML:可扩展标记语言

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

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

  • 现在一般都用json替代了

    1
    2
    3
    4
    5
    <student>
    <name>孙悟空<name/>
    <age>18</age>
    <gender></gender>
    </student>
    1
    2
    3
    4
    5
    {
    name:"孙悟空",
    age: 18,
    gender: 男
    }

    两者的作用是相同的

Ajax的特点

优点

  • 可以无需刷新页面与服务器端进行通信
  • 允许你根据用户触发的事件来更新部分页面内容

缺点

  • 没有浏览历史,不能回退
  • 存在跨域问题
  • SEO不友好
    • SEO:搜索引擎优化

Ajax的简单使用

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
<body>
<button>Click me to send request</button>
<div id="result"></div>
<script>
// 获取btn元素
const btn = document.getElementsByTagName('button')[0]
const result = document.getElementById('result')

btn.onclick = function(){
// 创建ajax对象
const xhr = new XMLHttpRequest()
// 初始化,设置请求方法和url
xhr.open('GET', 'http://127.0.0.1:8000/example')
// 发送
xhr.send()
// 事件绑定,处理服务器端返回的结果
// on when 当...时候
// readystate是xhr对象的属性:表示状态:
// 0未初始化 1open调用完毕 2send调用完毕 3服务端返回的部分结果 4服务端返回的所有结果
xhr.onreadystatechange = function(){
// 判断服务端返回所有结果
if(xhr.readyState === 4){
// 判断响应状态码:200 404...
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{
// 处理结果 行 头 空行 体
console.log(xhr.status)//状态码
console.log(xhr.statusText)//状态字符串
console.log(xhr.getAllResponseHeaders())//响应头
console.log(xhr.response)//响应体
}
}
}
}
</script>
</body>

在这里的服务端为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const express = require('express')

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

app.get('/example',(request, response)=>{
// 设置响应头,设置为允许跨域
response.setHeader('Access-Control-Allow-Origin','*')

response.send('message')
})

app.listen(8000, ()=>{
console.log('http://127.0.0.1:8000')
})

我们可以观察得知:在html页面获取请求,然后再根据服务端发送过来的response来进行重新渲染

POST型

我们需要先安装pody-parse

1
2
npm init -y
npm i -S body-parser

下面是代码部分

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
<body>
<button>Click me to send request</button>
<div id="result"></div>

<script>
const result = document.getElementById('result')
result.addEventListener('mouseover',()=>{
const xhr = new XMLHttpRequest()
xhr.open('POST', 'http://127.0.0.1:8000/example')
// 设置请求头:请求体类型 + 参数类型
// 一般会将身份信息放在请求头
// xhr.setRequestHeader('name','baidu')也是合法的,不过需要后端来改动
xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded')
// 发送
xhr.send('a=100&b=200')
xhr.onreadystatechange = () => {
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
result.innerHTML = xhr.response
}
}
}
})
</script>
</body>
1
2
3
4
5
app.post('/example',(request, response)=>{
response.setHeader('Access-Control-Allow-Origin','*')

response.send('message post')
})

第三章 Ajax与json格式数据

js文件部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const express = require('express')

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

app.all('/json-server',(request, response)=>{
response.setHeader('Access-Control-Allow-Origin','*')

const data = {
name: 'Linkaidi'
}

let str = JSON.stringify(data)
response.send(str)
})

app.listen(9000, ()=>{
console.log('http://127.0.0.1:9000')
})

在此,我们可以开设另一个js文件来存放数据,或者我们可以使用mongoose

HTML文件部分

就只是普通的文件,多了一点转换json格式的API

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
<body>
<button>Click me to send request</button>
<div id="result"></div>

<script>
window.onkeydown = function(){
const result = document.getElementById('result')
const xhr = new XMLHttpRequest()

xhr.responseType = 'json'

xhr.open('GET', 'http://127.0.0.1:9000/json-server')
xhr.send()

xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
// 手动转换
// let data = JSON.parse(xhr.response)
// console.log(data)

// 自动转换,因为有了上面的:xhr.responseType = 'json'
result.innerHTML = xhr.response.name
console.log(xhr.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
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<body>
<button>Click me to send request</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 = () => {
alert('请求超时')
}

// 没有网络
xhr.onerror = () => {
alert('没有网络!请重试!')
}

//-------------------------------------------------//

xhr.open('GET', 'http://127.0.0.1:9000/delay')
xhr.send()
xhr.onreadystatechange = () => {
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
result.innerHTML = xhr.response
}
}
}
})
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const express = require('express')

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

app.all('/delay',(request, response)=>{
response.setHeader('Access-Control-Allow-Origin','*')
setTimeout(()=>{
response.send('delay')
}, 5000)
})

app.listen(9000, ()=>{
console.log('http://127.0.0.1:9000')
})
  • 在该例子中,我们可以明白,服务端的 ‘delay’ 字符串是发送不过去的,因为超时了。

  • 在浏览器查看超时的情况:

    • offine:设置无网络连接,当然也可以在此设置各个形式的网络连接
    • status:查看request的状态,是否传输成功

手动取消

我们只需要在另一个组件绑定xhr.abort()函数就好

注意事项:

  • 当然,需要在请求发送之前才能使用!!!

  • 发送和取消发送的XMLHttpRequest()都必须是同一个,因此写的时候要放到函数的外面做全局

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <body>
    <button>Click me to send request</button>
    <button>Click me to cancel request</button>
    <div id="result"></div>

    <script>
    const btn = document.getElementsByTagName('button')[0]
    const btn1 = document.getElementsByTagName('button')[1]
    const result = document.getElementById('result')

    let xhr = null

    btn1.addEventListener('click', function(){
    xhr.abort()
    })
    </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
27
28
29
<body>
<button>Click me to send time over request</button>
<button>Click me to cancel request</button>
<button>Click me to send request</button>
<div id="result"></div>

<script>
const btn2 = document.getElementsByTagName('button')[2]

let xhr = null

btn2.addEventListener('click', function(){
const xhr = new XMLHttpRequest()
xhr.timeout = 2000
xhr.ontimeout = () => {
alert('请求超时了')
}
xhr.open('GET', 'http://127.0.0.1:9000/server')
xhr.send()
xhr.onreadystatechange = () => {
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
result.innerHTML = xhr.response
}
}
}
})
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const express = require('express')

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


app.all('/server',(request,response)=>{
response.setHeader('Access-Control-Allow-Origin','*')
setTimeout(()=>{
response.send('延时发送')
}, 3000)
})

app.listen(9000, ()=>{
console.log('http://127.0.0.1:9000')
})

由于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
btn2.addEventListener('click', function(){
// 如果已经发送,那么自动取消其他的
if(isSending){
xhr.abort()
}
xhr = new XMLHttpRequest()
isSending = true

xhr.timeout = 2000
xhr.ontimeout = () => {
alert('请求超时了')
}
xhr.open('GET', 'http://127.0.0.1:9000/server')
xhr.send()
xhr.onreadystatechange = () => {
if(xhr.readyState === 4){
// 发送成功,状态重置
isSending = false

if(xhr.status >= 200 && xhr.status < 300){
result.innerHTML = xhr.response
}
}
}
})

在此需要有几点要注意的:

  • 不管按多少次按钮,所有的XMLHttpRequest都必须是同一个,才可以取消,不然就是一直在进行创建导致重复
  • 状态的处理需要自行理解
  • 不同的请求我们最好在不同的js文件里面写,这样XMLHttpRequest就不会公用导致另一边的请求被取消

第六章 jQuery发送Ajax请求

jQuery的引入

1
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

jQuery的部分使用

  • get的使用及参数说明:

    • 第一个参数:url地址
    • 第二个参数:传入的表单数据,对象形式包装,与之前的send(‘a=100&b=200’)是一样的
    • 第三个参数:回调函数,用于写执行get成功之后会发生的事情,通常有从服务端获取到的数据data
    • 第四个参数:决定data为什么格式
    1
    2
    3
    4
    5
    6
    7
    <script>
    $('button').eq(0).click(() => {
    $.get('http://127.0.0.1:9000/server',{a: 100, b: 200}, (data) => {
    console.log(data)
    }, 'json')
    })
    </script>
  • post也是同理

    1
    2
    3
    4
    5
    6
    7
    8
    <script>
    // 这是获取到的数据转换成json格式
    $('button').eq(0).click(() => {
    $.post('http://127.0.0.1:9000/server',{a: 100, b: 200}, (data) => {
    console.log(data)
    }, 'json')
    })
    </script>

jQuery的通用方法发送Ajax请求

直接将要获取的数据封装成一个对象来进行ajax请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$('button').eq(1).click(() => {
$.ajax({
url: 'http://127.0.0.1:9000/server',
// 传输格式
dataType: 'json',
// 传输数据
data: {a: 100, b: 200},
type: 'GET',
// 成功回调
success: (data) => {
console.log(data)
},
// 失败回调
error: () => {
console.log('出错了')
},
// 时长
timeout: 2000,
headers: {c: 300, d: 400}
})
})

第七章 Axios工具包

安装引入Axios

  • npm
1
npm i axios
  • yarn
1
yarn add axios
  • CDN引入
1
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.js"></script>

使用Axios示例

使用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
<body>
<button>GET</button>
<button>POST</button>
<button>AJAX</button>
<script>
const btns = document.querySelectorAll('button')
axios.defaults.baseURL = 'http://127.0.0.1:9000'
btns[0].onclick = () => {
axios.get('/server',{
// url参数,类似于jquery的传参
// data: {a: 100, b: 200}
params: {
id: 100,
vip: 7
},
// 请求行
header: {
c: 300,
d: 400
},
}).then((value)=>{
console.log(value)
})
}
</script>
</body>

好处就是能将URL简化,也像jQuery一样操作

使用fetch函数发送请求

1
2
3
4
5
6
7
8
9
10
11
12
13
fetch('http://127.0.0.1:9000/server', {
// 请求方法
method: 'POST',
headers: {
c: 300,
d: 400
},
body: 'id=100&vip=7'
}).then(response => {
return response.json()
}).then(response => {
console.log(response)
})

我们需要获取后台数据,就要使用then

我们可以发现在发送POST请求时会首先发送一个OPTION请求?

  • 一般而言就是因为跨域请求

第八章 跨域

同源策略

同源策略最早是一种安全策略

同源:协议、域名、端口号三者必须保持一致

违背同源策略就会发生跨域

解决跨域问题

jsonp

  • jsonp是一个非官方的跨域解决方案,只支持get请求

  • jsonp的工作原理:页面一些 标签具有跨域能力:script,img,link,ifame。jsonp就是借助script

  • 具体操作:服务端发送的是函数调用即可

    1
    2
    3
    4
    5
    6
    7
    app.all('/jsonp', (request, response) => {
    const data = {
    name: 'Once'
    }
    let FAQ = JSON.stringify(data)
    response.end(`handle(${FAQ})`)
    })
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <body>
    <div id="result"></div>

    <script>
    handle = function(data){
    const result = document.getElementById('result')
    result.innerHTML = data.name
    }
    </script>
    <script src="http://127.0.0.1:9000/jsonp"></script>
    </body>

    注意:在这里必须得是json格式,不然会报错的

    下面是一个小案例:在文本框输入并让文本框变色,再取出服务器的数据渲染到html页面上

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <script>
    const input = document.getElementById('input')

    const handle = (data) => {
    input.style.border = "solid 1px red"
    const p = document.createElement('p')
    p.innerHTML = data.msg
    document.body.appendChild(p)
    }

    input.onblur = () => {
    // 获取文本框内容
    let username = this.value

    const script = document.createElement('script')
    script.src = 'http://127.0.0.1:9000/UC1'

    document.body.appendChild(script)
    }
    </script>
    1
    2
    3
    4
    5
    6
    7
    app.all('/UC1', (request, response) => {
    const data = {
    msg: '用户名已经存在'
    }
    let str = JSON.stringify(data)
    response.end(`handle(${str})`)
    })

jQuery

jQuery部分,活用的是callback

1
2
3
4
5
6
7
8
9
10
11
<script>
$('button').eq(0).click(() => {
// callback参数的固定写法
$.getJSON('http://127.0.0.1:9000/jquery-server?callback=?', (data) => {
$('#result').html(`
名称: ${data.name},
地址: ${data.city}
`)
})
})
</script>
1
2
3
4
5
6
7
8
9
10
app.all('/jquery-server', (request, response) => {
const data = {
name: 'lkd',
city: ['北京','上海','深圳']
}
let str = JSON.stringify(data)

let cb = request.query.callback
response.end(`${cb}(${str})`)
})

CORS

CORS:跨域资源共享,为官方的解决方案

  • 特点:不需要在客户端做任何特殊的操作,完全在服务器处理
  • 工作原理:通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应,以后就会对响应放行
1
2
3
4
5
// 设置为允许跨域
// 后面的 * 应该是一个url地址,表示能跨域访问的地址
response.setHeader('Access-Control-Allow-Origin','*')
response.setHeader("Access-Control-Allow-Headers", "*")
response.setHeader("Access-Control-Allow-Method", "POST")