ajax入门

Ajax

什么是ajax?

AJAX 代表异步的 JavaScript 和 XML(Asynchronous JavaScript And XML)。简单点说,就是使用 XMLHttpRequest 对象与服务器通信。它可以使用 JSON、XML、HTML 和文本文件等格式发送和接收数据。AJAX 最吸引人的就是它的“异步”特性,也就是说它可以在不重新刷新页面的情况下与服务器通信,交换数据,或更新页面

简单来说

AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容

AJAX 不需要任何浏览器插件,但需要用户允许 JavaScript 在浏览器上执行

XMLHttpRequest 只是实现 Ajax 的一种方式

AJAX 不是新的编程语言,而是一种使用现有标准的新方法

是⼀个 默认异步执⾏机制的功能,AJAX分为同步(async = false)和异步(async = true)

什么是同步请求?(false)

同步请求是指当前发出请求后,浏览器什么都不能做,
必须得等到请求完成返回数据之后,才会执行后续的代码,
相当于生活中的排队,必须等待前一个人完成自己的事物,后一个人才能接着办。
也就是说,当JS代码加载到当前AJAX的时候会把页面里所有的代码停止加载,页面处于一个假死状态,
当这个AJAX执行完毕后才会继续运行其他代码页面解除假死状态

什么是异步请求?(默认:true)

默认异步:异步请求就当发出请求的同时,浏览器可以继续做任何事,
Ajax发送请求并不会影响页面的加载与用户的操作,相当于是在两条线上,各走各的,互不影响。
一般默认值为true,异步。异步请求可以完全不影响用户的体验效果,
无论请求的时间长或者短,用户都在专心的操作页面的其他内容,并不会有等待的感觉。

ajax的使用

ajax实现的基本步骤

  1. 创建XMLHttpRequest对象,即创建一个异步调用对象.
  2. 创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
  3. 设置响应HTTP请求状态变化的函数.
  4. 发送HTTP请求.
  5. 获取异步调用返回的数据.
  6. 使用JavaScript和DOM实现局部刷新.

1、创建XMLHttpRequest对象

1
var xmlHttpRequest = new XMLHttpRequest();

2、创建HTTP请求

创建了XMLHttpRequest对象之后,必须为XMLHttpRequest对象创建HTTP请求,用于说明XMLHttpRequest对象要从哪里获取数据。通常可以是网站中的数据,也可以是本地中其他文件中的数据。

open()语法

创建HTTP请求可以使用XMLHttpRequest对象的open()方法,其语法代码如下所示:

1
XMLHttpRequest.open(method,URL,flag,name,password);
method 指定HTTP的请求方法,常用的方法为getpost
URL HTTP请求的URL地址
flag 指定是否使用异步方式。true表示异步、false表示同步,默认为true(可选)
name 用于输入用户名。如果服务器需要验证,则必须使用该参数(可选)
password 用于输入密码。若服务器需要验证,则必须使用该参数(可选)

send() 方法

  • 通过 XMLHttpRequest 对象的 send() 方法,将客户端页面的数据发送给服务端

  • 语法:

    1
    xhr.send([body])  //body: 在 XHR 请求中要发送的数据体,如果不传递数据则为 null

    如果使用GET请求发送数据的时候,需要注意如下:

    • 将请求数据添加到open()方法中的url地址中
    • 发送请求数据中的send()方法中参数设置为null

配置链接信息

3、设置响应HTTP请求状态变化的函数

创建完HTTP请求之后,应该就可以将HTTP请求发送给Web服务器了。然而,发送HTTP请求的目的是为了接收从服务器中返回的数据。从创建XMLHttpRequest对象开始,到发送数据、接收数据、XMLHttpRequest对象一共会经历以下5中状态。

1
2
3
4
5
readyState === 0 : 表示未初始化完成,也就是 open 方法还没有执行 
readyState === 1 : 表示配置信息已经完成,也就是执行完 open 之后
readyState === 2 : 表示 send 方法已经执行完成
readyState === 3 : 表示正在解析响应内容
readyState === 4 : 表示响应内容已经解析完毕,可以在客户端使用了

一个最基本的 ajax 请求就是上面三步 但是光有上面的三个步骤,我们确实能把请求发送到服务端
如果服务端正常的话,响应也能回到客户端 但是我们拿不到响应
如果想要拿到响应,我们需要有两个前提条件

  1. 本次 HTTP 请求是成功的,也就是我们下面要说的 http 状态码为 200 ~ 299
  2. ajax 对象也有自己的状态码,用来表示本次 ajax 请求中各个阶段

在通过Ajax的异步调用获得服务器端数据之后,可以使用JavaScript或DOM来将网页中的数据进行局部更新。

axios

axios是什么?

[Axios](axios中文文档|axios中文网 | axios (axios-js.com)) 是一个基于 promise 网络请求库,作用于node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.js http 模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。

本质上就是对XMLHttpRequest()对象的封装

axios的基本使用

1.引入axios.js文件

2.使用axios( )函数

1
2
3
4
5
axios({
url: '请求数据的地址'
}).then(res=>{
console.log(res) //使用.then()方法接收请求到的结果,对服务器接收到的数据进行后续处理
})

url

统一资源定位符(英语:Uniform Resource Locator,缩写:URL,或称统一资源定位器定位地址URL地址[1])俗称网页地址,简称网址,是因特网上标准的资源的地址(Address),如同在网络上的门牌。它最初是由蒂姆·伯纳斯-李发明用来作为万维网的地址,现在它已经被万维网联盟编制为因特网标准RFC 1738

url组成

以“https://zh.wikipedia.org:443/w/index.php?title=随机页面”为例,其中:

  1. https,是协议;
  2. zh.wikipedia.org,是服务器;
  3. 443,是服务器上的网络端口号;
  4. /w/index.php,是路径;
  5. ?title=Special:随机页面,是查询参数。

aixos查询参数

参数就是和后台交互的时候给他的一些信息

get请求

axios的params方法会把接收到的参数以字符串的形式拼接到请求地址的后面

1
2
3
4
5
6
7
8
9
axios({
url:'www.baidu.com', axios内部处理后会以 www.baidu.com?a=100&b=200 的形式给服务器发送请求获取数据// 在地址后面加一个 ?,然后以 key=value 的形式传递 // 两个数据之间以 & 分割
params:{
参数名1:参数值1,//a:100,
参数名2:参数值2//b:200
}
}).then(res=>{

})

这样服务端就能接受到两个参数 一个是 a,值是 100,一个是 b,值是 200

post 请求

post 请求的参数是携带在请求体中的,所以不需要在url 后面拼接

1
2
3
4
5
6
7
8
9
axios({
url:'www.baidu.com',
method: 'post',
data: {
参数名1:参数值1
}
}).then(res=>{
//接收请求到的数据并做后续的处理
})
  • 但是携带参数 getpost两个方式还是有区别的

GET 还是 POST?

与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用。

然而,在以下情况中,请使用 POST 请求:

  • 不愿使用缓存文件(更新服务器上的文件或数据库)
  • 向服务器发送大量数据(POST 没有数据量限制)
  • 发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠

常用请求方法

get(默认请求) 获取数据
post 数据提交
put 修改数据(全部)
delete 删除数据
patch 修改数据(部分)

axios错误处理

语法:在then( )方法的后面调用.catch( )方法捕获错误,传入回调函数并定义形参

1
2
3
4
5
6
7
axios({

}).then(res=>{

}).catch(error=>{
console.log(error)
})

http 请求报文

http的请求报文由四个部分组成

1.请求行

2.请求头

3.请求空行

4.请求体

请求行

请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔

请求头

请求头由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔。请求头部通知服务器有关于客户端请求的信息,典型的请求头有:

User-Agent:产生请求的浏览器类型。

Accept:客户端可识别的内容类型列表。

Host:请求的主机名,允许多个域名同处一个IP地址,即虚拟主机

请求空行

最后一个请求头之后是一个空行,发送回车符和换行符,通知服务器以下不再有请求头。

请求体

请求数据不在GET方法中使用,而是在POST方法中使用。POST方法适用于需要客户填写表单的场合。与请求数据相关的最常使用的请求头是Content-Type和Content-Length。

响应报文

概念:服务器按照http协议要求的格式,返回给浏览器的内容

和请求报文也是一样也是有四个部分组成

1.响应行(状态行):协议、HTTP 响应状态码、状态信息

2.响应头:以键值对的格式携带的附加信息,比如:Content-Type

3.响应空行:分隔响应头,空行之后的是服务器返回的资源

4.响应体:返回的资源

http响应码

状态码 说明
1xx 消息响应
2xx 成功响应
3xx 重定向
4xx 客户端错误
5xx 服务端错误

常见http响应码

200———请求成功

301——— 资源(网页等)被永久转移到其它URL

404———请求的资源(网页等)不存在

500———内部服务器错误,无法完成请求

表单信息收集插件

form-serialize

1
const data = serialize(form, { hash: true, empty: true })

参数1: 表单元素对象

参数2: 配置对象

  • hash: true 将收集到的数据封装成对象, false 将收集到的数据封装成查询字符串

  • empty: 如果用户没有输入指定的内容, 也获取

一般不用管他全部默认设置为true,后面好方便操作

深入axios实现原理

xhr使用步骤

  1. 创建xhr对象

    1
    const xhr = new XMLHttpRequest()
  2. 调用open方法,设置url和请求方法

    1
    xhr.open('GET', 'http://hmajax.itheima.net/api/province')
  3. 监听loadend事件,接受结果,

会在调用xhr.send方法发送请求后,不管成功与否都会执行loadend事件里面的回调函数

1
2
3
4
5
6
xhr.addEventListener('loadend', () => {
// xhr.response 就是响应的结果
//默认接收过来的是字符串,需要转换为对象方便使用
const data = JSON.parse(xhr.response)
console.log(data)
})
  1. 调用send方法,向服务器发起请求

    1
    xhr.send()

    get请求

    URLSearchParams转换查询参数

    使用步骤:

    创建URLSearchParams 对象

    1
    2
    3
    4
    const params = new URLSearchParams({
    参数名1:参数值1,
    参数名2:参数值2,
    })

    生成至指定格式的查询参数

    1
    const queryString = paramsObj.toString(params)

    post请求

请求头设置 Contnet -Type:application/json

1
xhr.setRequestHeader('Content-Type', 'application/json')

发送请求携带json字符串数据

1
xhr.send(JSON.stringify(obj))

promise

概念:**Promise** 对象表示异步操作最终的完成(或失败)以及其结果值

这是引用MDN上面的概念,十分的晦涩难懂,简单来说,所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息

promise基本使用

Promise对象是一个构造函数,用来生成promise实例

下面的代码是一个最基本简单的Promise实例对象

1
2
3
4
5
6
7
8
9
10
11
12
13
// 1. 创建 Promise 对象
const p = new Promise((resolve, reject) => {
// 2. 开启异步任务, 并将结果传递出去
setTimeout(() => {
// 异步任务只有两种结果: 成功 / 失败
const result = '这是一秒后的结果'
// return result // 带不出去
// resolve(result) // 带到 p.then 中去
reject(new Error('失败了')) // 带到 p.catch 中去
// 总结: resolve 会触发 .then() 回调函数的执行, reject 会触发 .catch() 回调函数的执行
// 如果成功了就调用 resolve, 失败了就调用 reject
}, 1000)
})

Promise对象接收一个函数作为参数: 回调函数, 该函数会立即执行

里面的回调函数有两个参数:(由 JavaScript 引擎提供,不用自己部署)

参数1: resolve 函数,将Promise 表示从发送请求时的pending状态变为resolve,会在异步操作成功时调用,并将结果作为参数传递出去

参数2: reject 函数, 将Promise 表示从发送请求时的pending状态变为reject,会在异步操作失败时调用,并将结果作为参数传递出去

Promise实例对象还有一个then方法和catch方法他是定义在原型对象身上的

then方法

在回调函数内执行 resolve 就会把结果带到这里

下面是一个小例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function runAsync(){
let p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('执行完成');
resolve('随便什么数据');
}, 2000);
});
return p;
}
runAsync().then(function(data){
console.log(data);
//后面可以用传过来的数据做些其他操作
//......
});

在runAsync()的返回上直接调用then方法,then接收一个参数,是函数,并且会拿到我们在runAsync中调用resolve时传的的参数。运行这段代码,会在2秒后输出“执行完成”,紧接着输出“随便什么数据”。

then里面的函数就跟我们平时的回调函数一个意思,能够在runAsync这个异步任务执行完成之后被执行。这就是Promise的作用了,简单来讲,就是能把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。

then链式调用

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
function runAsync1(){
let p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('异步任务1执行完成');
resolve('随便什么数据1');
}, 1000);
});
return p;
}
function runAsync2(){
let p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('异步任务2执行完成');
resolve('随便什么数据2');
}, 2000);
});
return p;
}
function runAsync3(){
let p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('异步任务3执行完成');
resolve('随便什么数据3');
}, 2000);
});
return p;
}
1
2
3
4
5
6
7
8
9
10
11
12
runAsync1()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return runAsync3();
})
.then(function(data){
console.log(data);
});

image-20230807223356135

在then方法中,你也可以直接return数据而不是Promise对象,在后面的then中就可以接收到数据了

1
2
3
4
5
6
7
8
9
10
11
12
runAsync1()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return '直接返回数据'; //这里直接返回数据
})
.then(function(data){
console.log(data);
});

image-20230807223718475

一个任务(即一个 Promise 对象)可以被多次使用 .then() 方法进行链式调用。

Promise 链中,每次调用 .then() 方法都会返回一个新的 Promise 对象,这个新的 Promise 对象会在上一个 Promise 成功并返回数据时被解析

catch方法

在回调函数内执行 reject就会把结果带到这里

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
function getNumber(){
let p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
var num = Math.ceil(Math.random()*10); //生成1-10的随机数
if(num<=5){
resolve(num);
}
else{
reject('数字大于5,失败了');
}
}, 2000);
});
return p;
}

getNumber()
.then(
function(data){
console.log('resolved');
console.log(data);
},
function(reason, data){
console.log('rejected');
console.log(reason);
}
);

getNumber函数用来异步获取一个数字,2秒后执行完成,如果数字小于等于5,我们认为是“成功”了,调用resolve修改Promise的状态。否则我们认为是“失败”了,调用reject并传递一个参数,作为失败的原因。

运行getNumber并且在then中传了两个参数,then方法可以接受两个参数,第一个对应resolve的回调,第二个对应reject的回调。所以我们能够分别拿到他们传过来的数据。

多次运行我们只会得到两种结果,要么是小于5,要么是大于5,失败了

这时候就会用到catch方法了

1
2
3
4
5
6
7
8
9
getNumber()
.then(function(data){
console.log('resolved');
console.log(data);
})
.catch(function(reason){
console.log('rejected');
console.log(reason);
});

效果和写在then的第二个参数里面一样。不过它还有另外一个作用:在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中

1
2
3
4
5
6
7
8
9
10
getNumber()
.then(function(data){
console.log('resolved');
console.log(data);
console.log(somedata); //此处的somedata未定义
})
.catch(function(reason){
console.log('rejected');
console.log(reason);
});

在resolve的回调中,我们console.log(somedata);而somedata这个变量是没有被定义的。如果我们不用Promise,代码运行到这里就直接在控制台报错了,不往下运行了

all方法

Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调

1
2
3
4
5
Promise
.all([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
console.log(results);
});

用Promise.all来执行,all接收一个数组参数,里面的值最终都算返回Promise对象。这样,三个异步操作的并行执行的,等到它们都执行完后才会进到then里面。那么,三个异步操作返回的数据哪里去了呢?都在then里面呢,all会把所有异步操作的结果放进一个数组中传给then,就是上面的results。所以上面代码的输出结果就是:

race的用法

all方法的效果实际上是「谁跑的慢,以谁为准执行回调」,那么相对的就有另一个方法「谁跑的快,以谁为准执行回调」,这就是race方法,这个词本来就是赛跑的意思。race的用法与all一样

假如我们的异步任务1延时1秒后执行,异步任务2和3都是延时2秒后执行

1
2
3
4
5
Promise
.race([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
console.log(results);
});

这三个异步操作同样是并行执行的。结果你应该可以猜到,1秒后runAsync1已经执行完了,此时then里面的就执行了

image-20230807230234058

在then里面的回调开始执行时,runAsync2()和runAsync3()并没有停止,仍旧再执行。于是再过1秒后,才会输出

这个race有什么用呢?使用场景还是很多的,比如我们可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作,

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
//请求某个图片资源
function requestImg(){
var p = new Promise(function(resolve, reject){
var img = new Image();
img.onload = function(){
resolve(img);
}
img.src = 'xxxxxx';
});
return p;
}

//延时函数,用于给请求计时
function timeout(){
var p = new Promise(function(resolve, reject){
setTimeout(function(){
reject('图片请求超时');
}, 5000);
});
return p;
}

Promise
.race([requestImg(), timeout()])
.then(function(results){
console.log(results);
})
.catch(function(reason){
console.log(reason);
});

requestImg函数会异步请求一张图片,我把地址写为”xxxxxx”,所以肯定是无法成功请求到的。timeout函数是一个延时5秒的异步操作。我们把这两个返回Promise对象的函数放进race,于是他俩就会赛跑,如果5秒之内图片请求成功了,那么遍进入then方法,执行正常的流程。如果5秒钟图片还未成功返回,那么timeout就跑赢了,则进入catch,报出“图片请求超时”的信息