JavaScript XHR、Fetch
服务器端渲染-前后端分离
- **服务器端渲染:**将html文件在后端,拼接好,将整个文件全部返回给前端
- 随着目前业务逻辑的复杂度提升,这种开发模式,会导致效率低下
- 同时,有时候前端页面仅修改一小部分数据,就需要将这个网页全部返回,无疑是对网络造成了部分压力
- 由于服务端渲染的弊端,因此有了 前后端分离的开发模式
- 前端通过静态服务器请求HTML文件,js脚本
- JS脚本中通过AJAX进行网络请求
- API服务器中写相应的API请求数据库中的数据
什么是HTTP协议
-
在维基百科中的解释
- 超文本传输协议 是一种分布式、协作式和超媒体信息系统的 应用层协议
- HTTP是万维网的数据通信的基础,设计HTTP最初的目的是为了提供一种 发布和接收HTML页面的方法
- 通过 HTTP或者HTTPS协议请求的资源由统一资源标识符来标识
-
HTTP是客户端(用户)和服务端(网站)之间请求和响应的标准
- 一次HTTP请求主要包括:请求(Request)和响应(Response)
- 请求:包含请求行、请求头和请求体
- 响应:包含响应行,响应头和响应体
HTTP的版本
- HTTP/0.9
- 发布于1991年
- 只支持GET请求方法获取文本数据,当时主要是为了获取HTML页面的内容
- HTTP/1.0
- 发布于1996年
- 支持POST、HEAD等请求方法,支持请求头、响应头等,支持更多种数据类型
- 但是浏览器的每次请求都需要与服务器建立一个TCP连接、请求处理完成后立即断开TCP连接,每次建立连接增加了性能损耗
- HTTP/1.1(目前使用最广泛的版本)
- 发布于1997年
- 增加了PUT、DELETE等请求方法
- 采用持久连接(Connection:keep-alive),多个请求可以共同用一个TCP
- HTTP/2.0
- 发布于2015年
- HTTP/3.0
- 发布于2018年
HTTP请求方式
HTTP Request Header
-
content-type是这次请求携带的数据类型
- **application/x-www-form-urlencoded:**表示数据被编码成以 ‘&’分割的键值对,同时以=分隔键和值
- **application/json:**表示一个JSON类型
- text/plain:表示文本类型
- application/xml:表示xml类型
- **multipart/form-data:**表示是上传文件
-
content-length:文件的大小长度,不用手动配置
-
keep-alive
- http是基于TCP协议的,但是通常在进行一次请求和响应结束后会立即中断
- 在http1.0中若想保持长连接,需要手动进行设置
- 在 http1.1中,所有的连接都是默认为connection:keep-alive
-
accept-encoding:告知服务器,客户端支持的压缩格式,服务器会返回相应的压缩格式的文件
-
**accept:**告知服务器,客户端可以接收的文件类型
-
**user-agent:**客户端的相关信息
HTTP Response响应状态码
AJAX发送请求
AJAX是异步的JavaScript和XML
它可以使用JSON、XML、HTML和text文本等格式发送和接收数据
XHR发送请求
- 如何来完成AJAX请求
- 创建网络请求的AJAX对象(使用XMLHttpRequest)
- 监听 XMLHttprequest对象状态的变化,或者监听 onload事件(请求完成时触发)
- 配置网络请求(通过 open方法)
- 发送 send网络请求
//创建AJAX对象
let xhr = new XMLHttpRequest();//监听XMLHttpRequest对象状态的变化
xhr.onreadystatechange = function () {console.log(xhr.response);
};//配置网络请求
//method:get/post/delete
//URL
xhr.open("GET", "http://123.207.32.32:8000/home/multidata");//发送send网络请求
xhr.send();
XHR状态变化的监听
事实上,我们在一次网络请求中,可以看到XHR的状态发生了很多次的变化
值 | 状态 | 描述 |
---|---|---|
0 | UNSENT | 代理被创建,但是尚未调用open()方法(不会被监听) |
1 | OPENED | open()方法已经被调用 |
2 | HEADERS_RECEIVED | send()方法已经被调用,并且头部和状态已经可以获得 |
3 | LOADING | 下载中:reponseText属性已经包含部分数据 |
4 | DONE | 下载操作已经完成 |
- 因此我们若想获取请求之后的数据,需要在XHR的状态码为4的时候进行获取(注意此状态码不是HTTP的状态码)
//创建AJAX对象
let xhr = new XMLHttpRequest();//监听XMLHttpRequest对象状态的变化
xhr.onreadystatechange = function () {//这样写,代码阅读性不是很好// if (xhr.readyState !== 4) return;//此写法,等同于上面的的写法XMLHttpRequest.DONE代表常量if (xhr.readyState !== XMLHttpRequest.DONE) return;console.log(xhr.response);
};//配置网络请求
//method:get/post/delete
//URL
xhr.open("GET", "http://123.207.32.32:8000/home/multidata");//发送send网络请求
xhr.send();
XHR发送同步请求
XHR发送请求,默认发送的是异步请求,即不会阻塞xhr.send()后面代码的执行
- 若想让XHR发送的是同步请求,即只有当请求拿到数据之后,才能执行
xhr.send()
后面的代码 - 在
xhr.open()
中传入第三个参数false
- 但是在实际开发中,使用的都是异步请求
//创建AJAX对象
let xhr = new XMLHttpRequest();//监听XMLHttpRequest对象状态的变化
xhr.onreadystatechange = function () {//这样写,代码阅读性不是很好// if (xhr.readyState !== 4) return;//此写法,等同于上面的的写法XMLHttpRequest.DONE代表常量if (xhr.readyState !== XMLHttpRequest.DONE) return;console.log(xhr.response);
};//配置网络请求
//method:get/post/delete
//URL
xhr.open("GET", "http://123.207.32.32:8000/home/multidata",false);//发送send网络请求
xhr.send();//在这里即可拿到相应的数据
console.log(xhr.response)
XHR其他事件的监听
除了
onreadystatechange
还有其他的事件可以监听
loadstart
:请求开始progress
:一个响应数据包到达,此时整个response body
都在response
中abort
:调用xhr.abort()
取消了请求error
:发生连接错误:域名解析错误或者跨域load
:请求完成
//可以代替onreadystatechange进行数据的监听
xhr.onload = function () {console.log(xhr.response);
};
timeout
:由于请求超时二取消了该请求(默认情况下是0,不会超时,手动设置后会有超时)loadend
:在load error timeout 或abort之后触发
XHR响应数据和响应类型
在发送网络请求,接收数据的时候,可以对数据的格式进行设置
- 通过
xhr.responseType
进行设置 - 不设置的时候,默认为
text
格式
//创建AJAX对象//监听XMLHttpRequest对象状态的变化//对响应数据格式进行设置
xhr.responseType = "json"//配置网络请求//发送send网络请求
XHR获取HTTP状态码
通过
xhr.status
进行获取状态码,xhr.statusText
状态描述
- 写在 监听XMLHttpRequest对象状态的变化中
//创建AJAX对象
let xhr = new XMLHttpRequest();//监听XMLHttpRequest对象状态的变化
xhr.onreadystatechange = function () {//这样写,代码阅读性不是很好// if (xhr.readyState !== 4) return;//此写法,等同于上面的的写法XMLHttpRequest.DONE代表常量if (xhr.readyState !== XMLHttpRequest.DONE) return;if (xhr.status >= 200 && xhr.status < 300) {//返回正常数据console.log(xhr.response);} else {//返回相应的错误信息console.log(xhr.status, xhr.statusText);}
};//配置网络请求
//method:get/post/delete
//URL
xhr.open("GET", "http://123.207.32.32:8000/home/multidataa");//发送send网络请求
xhr.send();
XHR传递参数的四种方式
- GET请求通过querystring进行请求
- 缺点:使用明文进行传输
//创建AJAX对象
let xhr = new XMLHttpRequest();//监听状态变化
xhr.onload = function () {console.log(xhr.response);
};//设置响应数据格式
xhr.responseType = "json";//配置网络请求
//get请求,通过querystring进行参数的传递
xhr.open("get","http://123.207.32.32:1888/02_param/get?name=why&name=18&address=广州市"
);//发送网络请求
xhr.send();
- POST请求
x-www-form-urlencoded
格式- 需要对发送的格式进行设置
xhr.setRequestHeader
- 需要对发送的格式进行设置
//创建AJAX对象
let xhr = new XMLHttpRequest();//监听状态变化
xhr.onload = function () {console.log(xhr.response);
};
//post请求,发送urlencoded
xhr.open("post", "http://123.207.32.32:1888/02_param/posturl");
//告知发送的是什么格式的数据
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");//发送网络请求
xhr.send("name=why&name=18&address=广州市");
- POST请求
FormData
格式- 默认发送的就是
FormData
格式的数据,因此不用设置
- 默认发送的就是
//HTML中有form表单,直接使用form表单即可
const formEl = document.getElementById("form")//创建AJAX对象
let xhr = new XMLHttpRequest();//监听状态变化
xhr.onload = function () {console.log(xhr.response);
};
//post请求,发送formData数据
xhr.open("post", "http://123.207.32.32:1888/02_param/posturl");const formData = new FormData(formEl)//发送网络请求
xhr.send(formData);
- POST请求
JSON
格式:常用- 需要将对象转成JSON字符串
- 同时告知服务器传递的是什么格式的数据
//创建AJAX对象
let xhr = new XMLHttpRequest();//监听状态变化
xhr.onload = function () {console.log(xhr.response);
};//设置响应数据格式
xhr.responseType = "json";//post请求,发送JSON格式数据
xhr.open("post", "http://123.207.32.32:1888/02_param/postjson");//告知服务器传递的JSON格式数据
xhr.setRequestHeader("Content-type", "application/json");//发送网络请求,传递JSON字符串
xhr.send(JSON.stringify({ name: "why", age: 18, height: 1.88 }));
XHR过期时间和取消请求
- 过期时间:xhr.timeout
- 不设置默认为0,代表没有过期时间
- 超过了设置的时间,浏览器没有得到相应的数据,就会取消请求
- 通过 xhr.ontimeout可以进行监听
- 取消请求:xhr.abort()
- 通过调用此方法可以取消网络请求
- 同时可以通过 xhr.onabort监听是否主动取消了网络请求
//创建AJAX对象
let xhr = new XMLHttpRequest();//监听状态变化
xhr.onload = function () {console.log(xhr.response);
};//设置响应数据格式
xhr.responseType = "json";//设置过期时间
xhr.timeout = 3000;
//监听过期时间
xhr.ontimeout = function () {console.log("时间过期了");
};//监听取消网络请求
xhr.onabort = function () {console.log("网络请求被取消了");
};//post请求,发送JSON格式数据
xhr.open("get", "http://123.207.32.32:1888/01_basic/timeout");//发送网络请求,传递JSON字符串
xhr.send();
XHR文件上传的接口演练
<input type="file" id="file" />
<button id="btn">上传</button><script>//获取元素let file = document.getElementById("file");let btn = document.getElementById("btn");//绑定事件btn.onclick = function () {//获取上传的文件let fileData = file.files;//准备上传数据let formData = new FormData();formData.append("test", fileData[0]);//实例化XHR对象let xhr = new XMLHttpRequest();//监听结果变化xhr.onload = function () {//获取结果console.log(xhr.response);};//网络请求设置xhr.open("POST", "123.207.32.32:1888/02_param/upload");//发送网络请求xhr.send(formData);};
</script>
认识Fetch和Fetch API
可以看作是早期的XMLHttpRequest的替代方案
-
Fetch的返回值是一个promise
- 因此可以通过.then获取结果
- 通过.catch获取错误请求
-
基本使用
//完成网络请求
fetch("http://123.207.32.32:8000/home/multidata").then((response) => {console.log(response);
});//获取具体数据(版本一)
fetch("http://123.207.32.32:8000/home/multidata").then((response) => {//返回的数据是json就调用json(),text就调用text()方法//该方法也是一个promise,使用.then获取具体内容response.json().then((data) => {console.log(data);});
});//以上版本的代码,在.then中调用了.then,因此可以进行以下优化
//通过链式调用的方式
fetch("http://123.207.32.32:8000/home/multidata").then((response) => {//返回的数据是json就调用json(),text就调用text()方法return response.json();}).then((data) => {console.log(data);});//以上代码依旧可以做出优化,使用async/await
async function getData() {let response = await fetch("http://123.207.32.32:8000/home/multidata");let data = await response.json();console.log(data);
}getData();
- 在发送网络请求中后,
response
中可以查看HTTP请求状态
console.log(response.status, response.ok, response.statusText);//200 true "OK"
post请求有参数
使用post请求,默认传递的是formData格式的数据,因此传递其它格式的数据需要进行设置
async function getData() {let response = await fetch("http://123.207.32.32:1888/02_param/postjson", {//设置请求方法method: "post",//设置请求头headers: {"Content-type": "application/json",},//设置请求体body: JSON.stringify({ a: 100 }),});let data = await response.json();console.log(data);
}getData();