XHR就是XMLHttpRequest,Fetch
前端数据请求方式
服务器端渲染
在早期的时候,页面都是由后端服务器渲染出来的,用户请求网址后,服务器端需要请求数据库拿到数据,提供jsp等技术把网页渲染组装完成,然后再发送给用户。
这样的话就太麻烦了,后端的工作量也非常的大。所以有了前后的分离。
通俗说就是用户请求页面后,服务器会从数据库拿到数据,然后提供jsp等模板配合数据渲染成一个html页面,然后把组装完成的完整页面返回给用户
前后的分离过程
前后端分离的方式就是客户端渲染,用户请求页面后,静态资源服务器会返回静态的html和js脚本等但是没有数据给客户端,然后客户端也就是浏览器会执行js,然后会请求后端服务器从数据库拿到数据后返回给客户端,客户端进行数据和静态页面的整合。
tip:
1-一些小项目,没有前端服务器,静态html,js和后端服务器放在一起。
2-提供数据的后端服务器也叫做api服务器
3-前端总说什么api接口,其实就是后端程序员写的程序,这个接口是给我们数据的,我们通过js去请求接口。
4-执行js去请求后端数据的时候,这个js就是fetch这些
前后的分离优势
服务器渲染的缺点:一点点数据发生变化,需要刷新页面重新请求,服务器端重新渲染一次整个页面,然后再把整个页面发送给客户端,会浪费很多资源和性能。
AJAX以前是指异步的JavaScript和XML,但是现在基本上数据不用XML传输,而是用JSON(JSON的全称是JavaScript Object Notation)notation符号的意思,代替,它可以实现不刷新页面,修改变化的一些数据
例子:比如qq空间等网页最下面的 ‘加载更多’ ,你点击一下会出现更多内容,但是页面并不会刷新,因为异步请求的原因,也不会影响到现有内容的使用,比如你加载更多的时候卡住了,照样可以看qq空间的其他东西。(同步任务放在执行栈,异步任务放在任务队列,异步任务不会阻塞同步任务)
但是:现在又流行起了服务器渲染,也就是SSR,后续我也会学。
Http协议的解析
五层协议详解https://blog.csdn.net/yikenaoguazi/article/details/107927588
网络分为五层,应用层,传输层,网络层,链路层,物理层。
http协议和ftp和dns协议处于应用层,传输层有udp和tcp协议,网络层有ip协议
1-http最初是用来发布和接收HTML页面的
2-http请求的资源,由url统一资源定位符来标识
3-http的默认端口是80,https的默认端口是443,端口也可以修改
4-https,就是在http上加上了一个安全层证书,SSL安全套接字协议层或者TLS,可以说tls是ssl的升级版。
HTTP的组成
请求包括请求行,请求头,请求体
响应有响应行,响应头,响应体
请求头
content-type可以告诉服务器,我们传输过去的数据是什么格式,方便服务器解析
如content-type:application/json
文本的话就是text/plain (plain平原,朴素的,清楚的)
在现在的开发中都用的http1.1,默认开启了一个tcp可以进行多个连接,我们在请求头中需要些connection:keep-alive,这个tcp连接存活时间服务器可以自己设置,node后端服务器
accept-encoding
accept-encoding是告诉服务器,我这个客户端支持的文件压缩格式,比如请求了一个js文件,服务器可以把这个文件再压缩成更小如gzip格式,就可以降低服务器压力。(这些压缩文件,浏览器支持自动解压)
问题:我们有服务器,我咋给每个js配置个这样的压缩文件?
在后续webpack中,可以设置。
accept
告知服务器可以接收的数据格式,现在一般是*/*
Accept:*/* 也就是说可以接收任意格式
accept-encoding一般是请求js这些文件的时候用到,accept是接收数据。
user-agent
这个里面包含了客户端的浏览器版本型号和电脑系统windows还是mac这一类数据。
响应头
响应中也有内容长度和内容类型的设置,其中Access-Control-Allow-Origin会涉及到跨域问题,后面会详细学。
http状态码
HTTP的版本
我们现在最常用的是http1.1版本,多个请求可以共用一个tcp连接
参考:https://www.jianshu.com/p/e0b39b52672c
新建一条记录的话就用post,
更新一条记录的话就用put.
put:如果请求URI(Request-URI)指定的的资源已经在源服务器上存在,那么此请求里的实体应该被当作是源服务器关于此URI所指定资源实体的最新修改版本。
put是修改替换了整个数据,patch请求是修改一部分数据。
AJAX发送请求
XHR的基本用法
<script>
// 1.创建XMLHttpRequest对象
const xhr = new XMLHttpRequest()
// 2.监听状态的改变,也就是说从发送请求变化到得到数据(宏任务)
//这里一样可以用addEventListner事件改成readychange就行了
//为什么这里都是小写?因为事件默认全部小写,如click
//实际上一次网络请求,这里监听事件触发了四次,有三次为123不满足条件4被return了
xhr.onreadystatechange = function() {
// console.log(xhr.response)
if (xhr.readyState !== XMLHttpRequest.DONE) return
// 将字符串转成JSON对象(js对象)
const resJSON = JSON.parse(xhr.response)
const banners = resJSON.data.banner.list
//这里拿到数据了,就可以用for循环什么的生成dom对象插入到页面等等操作
console.log(banners)
}
// 3.配置请求open
// 第一个参数method: 请求的方式(get/post/delete/put/patch...)
// 第二个参数url: 请求的地址
xhr.open("get", "http://123.207.32.32:8000/home/multidata")
// 4.发送请求(浏览器帮助发送对应请求)
xhr.send()
</script>
一次网络请求中,xhr对象的完成状态readyState发生了多次变化,一共有五种
0就是实例了XMLHttpRequest但是还没有open配置
1就是open方法被调用
2就是send方法被调用了,拿到了响应中的响应头
3在下载接口返回的数据了,但是没有下完
4后端接口返回的数据下载完毕
同步和异步请求
在配置open中有第三个参数,默认是true为异步,如果设置为false就会变成同步任务,那么就必须等待完成了这个请求,才能执行后面的代码。
// 3.配置请求open
// 第一个参数method: 请求的方式(get/post/delete/put/patch...)
// 第二个参数url: 请求的地址
xhr.open("get", "http://123.207.32.32:8000/home/multidata",false)
XMLHttpRequest的一些监听事件
问题:上面代码的接口返回的本来就是json类型的数据,为什么还要调用JSON.parse()去处理?
因为其实XMLHttpRequest对象中有一个responseType属性,如果我们不设置默认为text,就算服务器传过来的是json,在response中也被接收成了文本。
1-当我们接收的是json,可以设置xhr.responseType=’json’
2-当我们接收的是文本,那么我们不能设置xhr.responseType=’json’,就算传来了text,也会提示接收为null,因为底层无法让text转换为json,就会变成null
3-文本可以自动识别转换为xml,无法转换为json;json可以自动识别转换为text和xml;xml可以自动转换为文本,无法转换为json
4-我们也可以直接使用responseXML或者responseText,但是如果使用这两个,也不能设置xhr.responseType=’json’,因为说了接收的是json,你又输出XML/Text也是不行的。
<script>
// 1.
const xhr = new XMLHttpRequest()
// 2.onload监听数据加载完成
xhr.onload = function() {
// const resJSON = JSON.parse(xhr.response)
console.log(xhr.responseXML)
// console.log(xhr.responseText)
// console.log(xhr.responseXML)
}
// 3.告知xhr获取到的数据的类型
xhr.responseType = "json"
// xhr.responseType = "xml"
// 4.配置网络请求
// 4.1.json类型的接口
// xhr.open("get", "http://123.207.32.32:8000/home/multidata")
// 4.2.json类型的接口
// xhr.open("get", "http://123.207.32.32:1888/01_basic/hello_json")
// 4.3.text类型的接口
// xhr.open("get", "http://123.207.32.32:1888/01_basic/hello_text")
// 4.4.xml类型的接口
xhr.open("get", "http://123.207.32.32:1888/01_basic/hello_xml")
// 5.发送网络请求
xhr.send()
</script>
获取HTTP请求的网络状态
当然你要等请求完毕才能用这个,不然根本就没有状态码,所以需要监听load事件或者xhr.readyState == XMLHttpRequest.DONE
//我们可以通过xhr.status/xhr.statusText来获取
比如成功,xhr.status会输出200
xhr.statusText会输出“OK”两个字
<script>
// 1.创建对象
const xhr = new XMLHttpRequest()
// 2.监听结果
xhr.onload = function() {
console.log(xhr.status, xhr.statusText)
// 根据http的状态码判断是否请求成功
if (xhr.status >= 200 && xhr.status < 300) {
console.log(xhr.response)
} else {
console.log(xhr.status, xhr.statusText)
}
}
xhr.onerror = function() {
console.log("onerror", xhr.status, xhr.statusText)
}
// 3.设置响应类型
xhr.responseType = "json"
// 4.配置网络请求
// xhr.open("get", "http://123.207.32.32:8000/abc/cba/aaa")
xhr.open("get", "http://123.207.32.32:8000/home/multidata")
// 5.发送网络请求
xhr.send()
</script>
他这里无论请求成功还是失败都会触发onload事件,失败的时候也会触发error事件,status用它们两个监听输出都可以。
GET/POST请求传递参数
其中get会明文显示在地址栏,post请求是把请求放在请求体里面了,记得设置xhr.setRequestHeader(“Content-Type,“application/x-www-form-urlencoded”),不会在地址栏显示
方法一
// 1.传递参数方式一: get -> query
// xhr.open("get", "http://123.207.32.32:1888/02_param/get?name=why&age=18&address=广州市")
把请求写在open的url参数段后面,get?xxxxxxxxx
方式二
// 2.传递参数方式二: post -> urlencoded
// xhr.open("post", "http://123.207.32.32:1888/02_param/posturl")
// // 发送请求(请求体body)
// xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
// xhr.send("name=why&age=18&address=广州市")
open配置中,请求方式改为post,url就写请求的地址,后面不加请求内容
把请求内容写入xhr.send(),记住必须设置请求头的内容类型,否则服务器无法解析识别内容
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
方式三
使用formdata文件传递
<body>
<form class="info">
<input type="text" name="username">
<input type="password" name="password">
</form>
<button class="send">发送请求</button>
<script>
const formEl = document.querySelector(".info")
const sendBtn = document.querySelector(".send")
sendBtn.onclick = function() {
// 创建xhr对象
const xhr = new XMLHttpRequest()
// 监听数据响应
xhr.onload = function() {
console.log(xhr.response)
}
// 配置请求
xhr.responseType = "json"
// 1.传递参数方式一: get -> query
// xhr.open("get", "http://123.207.32.32:1888/02_param/get?name=why&age=18&address=广州市")
// 2.传递参数方式二: post -> urlencoded
// xhr.open("post", "http://123.207.32.32:1888/02_param/posturl")
// // 发送请求(请求体body)
// xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
// xhr.send("name=why&age=18&address=广州市")
// 3.传递参数方式三: post -> formdata
// xhr.open("post", "http://123.207.32.32:1888/02_param/postform")
// // formElement对象转成FormData对象
// const formData = new FormData(formEl)
// xhr.send(formData)
// 4.传递参数方式四: post -> json
xhr.open("post", "http://123.207.32.32:1888/02_param/postjson")
xhr.setRequestHeader("Content-type", "application/json")
xhr.send(JSON.stringify({name: "why", age: 18, height: 1.88}))
}
</script>
</body>
也就是说把整个form的dom对象通过(这里必须是form表单类型的dom对象,也可以不写,直接formData.append())
const formData = new FormData(formEl)
转换成了一个FormData对象,然后把这个对象传递给xhr.send(),里面包含的input等内容。这种方法不需要再设置setRequestHeader,因为不设置默认就是form-data方式,这里的xhr.send(formData)就是这种方式。
方法四
传递json格式
// 4.传递参数方式四: post -> json
xhr.open("post", "http://123.207.32.32:1888/02_param/postjson")
xhr.setRequestHeader("Content-type", "application/json")
xhr.send(JSON.stringify({name: "why", age: 18, height: 1.88}))
我们拿到json后需要把它转换为json的字符串,服务器才能识别。为什么这样?因为就是这样设置的,也看后端接口是怎么写的根据情况定。
ajax网络请求封装
一般不用自己封装,我们会使用axios/fetch库,但是我们也需要了解它的底层原理。
作用:我们发送一个网络请求,通常有五步
// 1.创建对象
const xhr = new XMLHttpRequest()
// 2.监听数据
xhr.onload = function() {
}
// 3.设置类型
xhr.responseType = "json"
// 4.open方法
xhr.open()
//5,发送
xhr.send()
一个网页那么多请求,为了重复书写一样的代码,就有了封装,把传递进来的未知内容修改就好了,像个函数一样。
<script>
// 练习hyajax -> axios
function hyajax({
url,
method = "get",
data = {},
headers = {}, // token
success,
failure
} = {}) {
// 1.创建对象
const xhr = new XMLHttpRequest()
// 2.监听数据
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
success && success(xhr.response)
} else {
failure && failure({ status: xhr.status, message: xhr.statusText })
}
}
// 3.设置类型
xhr.responseType = "json"
// 4.open方法
if (method.toUpperCase() === "GET") {
const queryStrings = []
for (const key in data) {
queryStrings.push(`${key}=${data[key]}`)
}
url = url + "?" + queryStrings.join("&")
xhr.open(method, url)
xhr.send()
} else {
xhr.open(method, url)
xhr.setRequestHeader("Content-type", "application/json")
xhr.send(JSON.stringify(data))
}
return xhr
}
// 调用者
hyajax({
url: "http://123.207.32.32:1888/02_param/get",
method: "GET",
data: {
name: "why",
age: 18
},
success: function(res) {
console.log("res:", res)
},
failure: function(err) {
// alert(err.message)
}
})
// hyajax({
// url: "http://123.207.32.32:1888/02_param/postjson",
// method: "post",
// data: {
// name: "jsondata",
// age: 22
// },
// success: function(res) {
// console.log("res:", res)
// },
// failure: function(err) {
// // alert(err.message)
// }
// })
</script>
Timeout和取消请求
我们可以通过xhr.timeout=1000来取消请求,也就是说如果超过了1s服务器没有返回数据,自动取消此请求
也可以通过xhr.abort()来手动取消,比如把它写入一个按钮点击事件
<body>
<button>取消请求</button>
<script>
const xhr = new XMLHttpRequest()
xhr.onload = function() {
console.log(xhr.response)
}
xhr.onabort = function() {
console.log("请求被取消掉了")
}
xhr.responseType = "json"
// xhr.timeout=1000
// 1.超时时间的设置
xhr.ontimeout = function() {
console.log("请求过期: timeout")
}
// timeout: 浏览器达到过期时间还没有获取到对应的结果时, 取消本次请求
// xhr.timeout = 3000
xhr.open("get", "http://123.207.32.32:1888/01_basic/timeout")
xhr.send()
// 2.手动取消结果
const cancelBtn = document.querySelector("button")
cancelBtn.onclick = function() {
xhr.abort()
}
</script>
</body>
FetchAPI的使用详解
fetch和axios的区别https://blog.csdn.net/weixin_40016215/article/details/117322523
fetch需要用到promise,这个我原先学的黑马高级里面没有,后续有时间会学习coderwhy老师的2022高级补充
前端文件上传流程
XHR上传
formData.append()方法https://developer.mozilla.org/zh-CN/docs/Web/API/FormData/append
<script>
// xhr/fetch都可以文件上传,但是fetch无法看到上传进度
const uploadBtn = document.querySelector(".upload")
uploadBtn.onclick = function() {
// 1.创建对象
const xhr = new XMLHttpRequest()
// 2.监听结果
xhr.onload = function() {
console.log(xhr.response)
}
xhr.onprogress = function(event) {
console.log(event)
}
xhr.responseType = "json"
xhr.open("post", "http://123.207.32.32:1888/02_param/upload")
// 表单
const fileEl = document.querySelector(".file")
//这里的files可以拿到那个file类型input的一些数据信息
const file = fileEl.files[0]
const formData = new FormData()
formData.append("avatar", file)
xhr.send(formData)
}
</script>
其中XHR中有一个 onprogress里面可以看上传的进度
xhr.onprogress = function(event) {
console.log(event)
}
这个event里面包括很多东西,也包括了上传进度
fetch上传
<script>
// xhr/fetch
const uploadBtn = document.querySelector(".upload")
uploadBtn.onclick = async function() {
// 表单
const fileEl = document.querySelector(".file")
const file = fileEl.files[0]
const formData = new FormData()
formData.append("avatar", file)
// 发送fetch请求
const response = await fetch("http://123.207.32.32:1888/02_param/upload", {
method: "post",
body: formData
})
const res = await response.json()
console.log("res:", res)
}
</script>