1.Ajax,Fetch与跨域请求
1. Ajax基础
1.1 初始Ajax
1.Ajax是什么?
Ajax是Asynchronous JavaScript and XML (异步JavaScript和XML) 的简写
Ajax中的异步:可以异步地向服务器发送请求,在等待响应的过程中,不会阻塞当前页面,浏览器可以做自己的事情,直到成功获取响应后,浏览器才开始处理响应数据
XML(可扩展标记语言) :是前后端数据通信时传输的一种格式
XML现在不怎么用了,现在比较常用的时JSON
Ajax其实就是浏览器与服务器之间的一种异步通信方式;使用Ajax可以在不重新加载整个页面的情况下,对页面的某个部分进行更新
举例子:1.注册时检测手机号 2.搜索提示...
2.搭建Ajax开发环境
Ajax需要服务器环境,非服务器环境下,很多浏览器无法正常使用Ajax
vscode插件:Live Server插件安装,右键Open with Live Server打开html文件
1.2 Ajax的基本用法
1.XMLHttpRequest
Ajax想要实现浏览器与服务器之间的异步通信,需要依靠XMLHttpRequest,它是一个构造函数
2.Ajax的使用步骤
2.1 创建xhr对象【new XMLHttpRequest()】
const xhr=new XMLHttpRequest()
2.2 准备发送请求【.open(http请求方法,请求地址,是否使用异步请求)】
调用open并不会真正发送请求,而只是做好发送请求前的准备工作
const xhr=new XMLHttpRequest()
//发起请求
xhr.open('HTTP方法:GTE,POST,PUT,DELETE',地址 url,是否使用异步方式true)
2.3 发送请求【.send()返回的是服务器响应的数据,传递的参数一般是字符串】
send()参数是通过请求体携带数据,get请求不需要传(一般传null),post需要传请求体
const xhr=new XMLHttpRequest()
//准备发起请求
xhr.open('HTTP方法:GTE,POST,PUT,DELETE',地址 url,是否使用异步方式true)
//发送请求
xhr.send()//get请求可以传null,post请求传入携带的数据
2.4 监听事件,来处理响应【xhr.readystatechange(){}监听xhr.readyState的值&判断xhr.status(http状态码)】
当获取到响应后,会触发xhr对象的readystatechange事件,可以在该事件中对响应进行处理
xhr.readystatechange事件监听xhr.readyState的状态变化,它的值有0~4,一共5个状态,每一次状态变化都会触发readystatechange这个事件
0:未初始化。尚未调用open()
1:启动。已经调用open(),但尚未调用send()
2:发送。已经调用send(),但是尚未接到响应
3:接收。已经接收到部分响应
4:完成。已经接收到全部响应数据,而且已经可以在浏览器中使用了
获取响应数据后,响应内容会自动填充到xhr对象的属性
xhr.status就是http code,状态码代表含义如下:
100~199 消息:代表请求已经被接受,需要继续处理
101:websocket Vscode的插件live Server
200~299 成功:请求响应成功
200
300~399 重定向
301:永久重定向,重定向的地址会被浏览器缓存,除非你手动清除缓存
302:临时重定向,每一次都会向服务器确定一下,接下来向哪里跳转
304:没有修改,本地有一份缓存,浏览器向服务器发送请求,确认过没过期,表示文件没有被修改还是以前的文件
400~499 请求错误【这个错误一般是在前端】
404没有找到
500~599 服务器错误
500
xhr.statusText:HTTP状态说明(OK ,Not Found)
xhr.responseText:得到响应的数据,数据的字符串形式
const xhr=new XMLHttpRequest()
//监听响应
xhr.addEventListener("readystatechange",()=>{})
或
xhr.onreadysatechange=()=>{
//当readyState==4时,响应数据接收完毕
if(xhr.readyState!==4)return;
//判断请求服务器是否出现错误 HTTP CODE
if((xhr.status>=200 & xhr.status<300)||xhr.status===304){
console.log("正常使用响应数据了")
console.log(xhr.responseText)
}
}
//准备发起请求
xhr.open('HTTP方法:GTE,POST,PUT,DELETE',地址 url,是否使用异步方式true)
//发送请求
xhr.send()//get请求可以传null,post请求传入携带的数据
3.使用Ajax完成前后端通信
这里自己可以搭建一个后台服务,模拟前后端通信,我这里借用的时nodejs+express搭建的,下面的代码可能会报错,因为会有跨域的问题,我测试的使用是后端处理的跨域。
<script>
const xhr=new XMLHttpRequest()
xhr.onreadystatechange=()=>{
if(xhr.readyState!==4)return;
if((xhr.status>=200&&xhr.status<300)||xhr.status===304){
console.log("拿到响应数据了",xhr.responseText)
console.log("拿到响应数据的数据类型:",typeof xhr.responseText)//string
}
}
xhr.open("get","http://localhost:3000/list",true)
xhr.send(null)
</script>
1.3 GET请求
1.携带数据
GET请求不能通过请求体携带数据,但可以通过请求头携带
//表单提交 需要带name属性
<form action="http://localhost:3000/list" method="get">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit" value="提交">
</form>
<script>
const xhr=new XMLHttpRequest()
xhr.onreadystatechange=()=>{
if(xhr.readyState!==4)return;
if((xhr.status>=200&&xhr.status<300)||xhr.status===304){
console.log("拿到响应数据了",xhr.responseText)
console.log("拿到响应数据的数据类型:",typeof xhr.responseText)//string
}
}
xhr.open("get","http://localhost:3000/list?id=1&name=lisi",true)
xhr.send(null)
</script>
2.数据编码
如果携带的数据是非英文的话,比如说汉字,就需要编码之后再发送给后端了,不然会造成乱码问题,可以使用encodeURIComponent()编码
<script>
const xhr=new XMLHttpRequest()
xhr.onreadystatechange=()=>{
if(xhr.readyState!==4)return;
if((xhr.status>=200&&xhr.status<300)||xhr.status===304){
console.log("拿到响应数据了",xhr.responseText)
console.log("拿到响应数据的数据类型:",typeof xhr.responseText)
}
}
//数据编码
xhr.open("GET",`http://localhost:3000/list?id=1&name=${encodeURIComponent('前端')}`,true)
xhr.send(null)
</script>
1.4 POST请求
1.携带数据【send(请求体携带的数据)】
POST请求主要通过请求体携带数据,同时也可以通过请求头写携带数据
如果想发送数据,直接写再send()参数位置,一般是字符串,不能直接传递对象,需要先将对象转换为字符串的形式【JSON.stringify()】
<script>
const xhr=new XMLHttpRequest()
xhr.onreadystatechange=()=>{
if(xhr.readyState!==4)return;
if((xhr.status>=200&&xhr.status<300)||xhr.status===304){
console.log("拿到响应数据了",xhr.responseText)
console.log("拿到响应数据的数据类型:",typeof xhr.responseText)
}
}
//数据编码
xhr.open("POST",`http://localhost:3000/list?id=1&name=${encodeURIComponent('前端')}`,true)
//传二种格式--
//xhr.send('username=lisi&age=18')//第一种:类似于form表单的POST请求
xhr.send({
username:'elen',
age:18
})//第二种:[object Object]字符串
</script>
2.数据编码
<script>
const xhr=new XMLHttpRequest()
xhr.onreadystatechange=()=>{
if(xhr.readyState!==4)return;
if((xhr.status>=200&&xhr.status<300)||xhr.status===304){
console.log("拿到响应数据了",xhr.responseText)
console.log("拿到响应数据的数据类型:",typeof xhr.responseText)
}
}
xhr.open("POST",`http://localhost:3000/list?category=${encodeURIComponent('前端')}`,true)
//数据编码
xhr.send(`username=${encodeURIComponent('张三')}&age=18`)
</script>
2. JSON
JSON必须是双引号
2.1 初识JSON
1.JSON是什么
Ajax发送和接收数据的一种格式;XML,username=alen&age=18,JSON
JSON全称是JavaScript Object Notation
JSON格式--这一种长得像js的对象
{
"code":200,
"msg":'数据响应成功'
"data":[
{
"age":18
}
]
}
2.为什么需要JSON
JSON有三种形式,每种形式的写法都和 JS 中的数据类型很像,可以很轻松的和 JS的数据类型互相转换
JS---->JSON---->PHP/Java
PHP/Java--->JSON----->JS
利于前后端之间相互通信
2.2 JSON的三种形式【简单值形式,对象形式,数组形式】
1.简单值形式【JSON的简单值形式就对应着JS中基础数据类型】
JSON的简单值形式就对应着JS中基础数据类型(数字,字符串,布尔值,null,undefined除外)
注意事项:
JSON中没有undefined值,
JSON中字符串必须使用双引号,
JSON中是不能有注释的
//index.json文件
5
或者
"str"
或者
true
或者
null
...
2.对象形式【JSON的对象形式就对应着JS的对象】
JSON的对象形式就对应着JS的对象
注意事项:
JSON对象的属性名必须用双引号,属性值如果是字符串也必须用双引号;
JSON中只要涉及到字符串,就必须是双引号
不支持undefined
//举例子
{
"data":{
"name":"张三",
"age":18
},
"hobby":["足球","篮球"],
"isMale":true
}
3.数组形式【JSON的数组形式就对应着JS的数组】
注意事项:
数组中字符串必须用双引号
JSON中只要设计到字符串,就必须使用双引号
不支持undefined
[
{
"name":"芜湖",
"age":18
},
18,
"你好",
true,
["你好",99,null,false]
]
2.3 JSON的常用方法【JSON.parse()与JSON.stringify】
1.JSON.parse()【将JSON格式的字符串转化为JS中对应的数据类型】
将JSON格式的字符串转化为JS中对应的数据类型
const xhr=new XMLHttpRequest()
xhr.onreadystatechange=()=>{
if(xhr.readyState!==4)return;
if((xhr.status>=200&&xhr.status<300)||xhr.status===304){
const data=JSON.parse(xhr.responseText)
console.log("JSON格式的数据转换为对应的JS数据类型",data,typeof data)
}
}
xhr.open("GET","./index.json",true)
xhr.send(null)
2.JSON.stringify()【将JS的数据类型转化为JSON格式的形式】
将JS的数据类型转化为JSON格式的形式
const xhr=new XMLHttpRequest()
xhr.onreadystatechange=()=>{
if(xhr.readyState!==4)return;
if((xhr.status>=200&&xhr.status<300)||xhr.status===304){
const data=JSON.parse(xhr.responseText)
console.log("JSON格式的数据转换为对应的JS数据类型",data,typeof data)
}
}
xhr.open("GET","./index.json",true)
xhr.send(JSON.stringify({
username:'alen',
age:19
}))
3.JSON.parse() 和 JSON.stringify() 封装 localStorage(值需要是字符串)
//storage.js文件
const storage=window.localStorage
//设置
const set=(key,value)=>{
storage.setItem(key,JSON.stringify(value))
}
//读取
const get=(key)=>{
return storage.getItem(JSON.parse(key))
}
//删除
const remove=(key)=>{
storage.removeItem(key)
}
//删除全部
const clear=()=>{
storage.clear()
}
export {set,get,remove,clear}
//index.html
<script type='module'>
import {get,set,remove,clear} from './storage.js'
set("username","alen")
console.log(get("username"))
...
</script>
3. 跨域
3.1 初识跨域
1.跨域是什么?【不同协议||不同域名||不同端口号】
向一个域发送请求,如果要请求的域和当前域不是同域,就叫跨域;不同域之间请求,就是跨域请求
//同域,不是跨域
//search.html文件
<body>
<h1>同域</h1>
</body>
//index.html文件
<script>
const url='./search.html'
const xhr=new XMLHttpRequest()
xhr.onreadystatechange=()=>{
if(xhr.readyState!==4)return;
if((xhr.status>=200&&xhr.status<300)||xhr.status==304){
console.log(xhr.responseText)
}
}
xhr.open("GET",url,true)
xhr.send(null)
</script>
2.什么是不同域,什么是同域?
举例子:https【协议】://www.rongming.top【域名或者主机IP】:443【端口号】/course/list【路径】
协议,域名,端口号,任何一个不一样,就是不同域; 与路径无关,路径不一样无所谓
3.跨域请求为什么会被阻止?
阻止跨域请求,其实是浏览器本身的一种安全策略---同源策略
其他客户端或者服务器都不存在跨域被阻止的问题
4.跨域解决方案
1.CORS跨域资源共享
2.JSONP (script标签)
优先使用CORS跨域资源共享,如果浏览器不支持CORS的话,在使用JSONP
3.2 CORS跨域资源共享
1.CORS是什么
跨域资源共享,后端解决
Access-Control-Allow-Origin: * ;表明允许所有的域名来跨域请求它,*是通配符,没有任何限制
只允许指定域名的跨域请求:Access-Control-Allow-Origin:http://127.0.0.1:3000
2.使用CORS跨域的过程【后端解决】
2.1 浏览器发送跨域请求
2.2 后端再响应头中添加 Access-Control-Allow-Origin 头信息
2.3 浏览器接收到响应
2.4 如果是同域下的请求,浏览器不会额外做什么,这次前后端通信就圆满完成了
2.5 如果是跨域请求,浏览器会从响应头中查找是否允许跨域访问
2.6 如果允许跨域,通信圆满完成
2.7 如果没找到或不包含想要跨域的域名,就丢弃响应结果
3.CORS的兼容性
IE10及以上版本的浏览器可以正常使用CORS
关于跨域推荐网址:caniuse.com/
3.3 JSONP
1.JSONP的原理
script标签跨域不会被浏览器阻止,JSONP主要就是利用script标签,加载跨域文件
2.使用JSONP实现跨域【前声明后调用】
前端script标签声明一个函数;后端写一个JSONP接口,是前端声明的函数调用,调用时传入参数即可;这样前端就可以获取到后端传过来的数据
<script>
//2.动态加载
const script=document.createElement("script")
script.src="http://static-d0fd619d-b424-42b7-9eb4-12d4070b76d1.bspapp.com/jsonp.js"
document.body.appendChild(script)
//1.声明函数-对应后端JSONP的函数调用
const handleResponse=data=>{
console.log(data)
}
</script>
//1.手动加载
//后端JSONP接口
<script src="http://static-d0fd619d-b424-42b7-9eb4-12d4070b76d1.bspapp.com/jsonp.js"></script>
4. XHR对象
4.1 XHR的属性
1.responseType 和response属性【响应有关】
xhr.responseText:文本形式的响应内容,只能在没有responseType或者responseType=""或"text"的时候才能使用
xhr.response:可以替代responseText
xhr.responseType:响应的数据类型,准备发送请求之后,发送请求之前
responseType和responseIE6~9不支持,IE10开始支持
<script>
const url='http://localhost:3000/list'
const xhr=new XMLHttpRequest()
xhr.onreadystatechange=()=>{
if(xhr.readyState!==4)return;
if((xhr.status>=200&&xhr.status<300)||xhr.status==304){
//文本形式的响应内容
//console.log(xhr.responseText)
//响应数据类型不是文本使用xhr.response
console.log(xhr.response)
}
}
xhr.open("GET",url,true)
//准备之后,发送之前
//设置响应类型
//xhr.responseType=""//默认为空
//xhr.responseType="text"//字符串文本类型
xhr.responseType="json"//直接要解析过的JSON类型
xhr.send(null)
</script>
2.timeout属性【设置请求超时时间】
设置请求的超时时间(单位:ms);IE6~7不支持,IE8开始支持
<script>
const url='https://www.imooc.com/api/http/search/suggest?words=js'
const xhr=new XMLHttpRequest()
xhr.onreadystatechange=()=>{
if(xhr.readyState!==4)return;
if((xhr.status>=200&&xhr.status<300)||xhr.status==304){
console.log(xhr.response)
}
}
xhr.open("GET",url,true)
//准备之后,发送之前
xhr.timeout=10000
xhr.send(null)
</script>
3.withCredentials属性【请求Cookie有关】
指定使用Ajax发送请求的时候是否携带Cookie;使用Ajax发送请求,默认情况下,同域时,会携带Cookie,跨域时,不会,最终能否成功跨域携带Cookie,还要看服务器同不同意
xhr.withCredentials=true
后端:Access-Control-Allow-Origin:指定前端访问的域名
IE6~9不支持,IE10开始支持
<script>
const url='https://www.imooc.com/api/http/search/suggest?words=js'
const xhr=new XMLHttpRequest()
xhr.onreadystatechange=()=>{
if(xhr.readyState!==4)return;
if((xhr.status>=200&&xhr.status<300)||xhr.status==304){
console.log(xhr.response)
}
}
xhr.open("GET",url,true)
//准备之后,发送之前
//响应类型
//xhr.responseType=""//默认为空
//请求超时时间
xhr.timeout=10000
//解决跨域携带Cookie
xhr.withCredentials=true
xhr.send(null)
</script>
4.2 XHR的方法
1.abort()【终止当前请求】
一般配合abort事件一起使用
<script>
const url='https://www.imooc.com/api/http/search/suggest?words=js'
const xhr=new XMLHttpRequest()
xhr.onreadystatechange=()=>{
if(xhr.readyState!==4)return;
if((xhr.status>=200&&xhr.status<300)||xhr.status==304){
console.log(xhr.response)
}
}
xhr.open("GET",url,true)
//准备之后,发送之前
//响应类型
//xhr.responseType=""//默认为空
//请求超时时间
xhr.timeout=10000
//解决跨域携带Cookie
xhr.withCredentials=true
xhr.send(null)
//发送完请求之后调用
xhr.abort()//终止当前请求
</script>
2.setRequestHeader()【设置请求头信息】
可以设置请求头信息;xhr.setRequestHeader(头部字段名称,头部字段的值)
请求头中的Content-Type字段用来告诉服务器,浏览器发送的数据是什么格式的
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),对应数据发送格式为:xhr.send("username=alen&age=18")类似于form表单POST提交
xhr.setRequestHeader("Content-Type","application/json"),对应数据发送格式为:xhr.send(JSON.stringify({username:'alen'}))
<form action="https://www.imooc.com/api/http/search/suggest?words=js" method="post" enctype="application/x-www-form-urlencoded">
<input type="text" name="username" />
<input type="password" name="password" />
<input type="submit" value="提交" />
</form>
<script>
const url='https://www.imooc.com/api/http/search/suggest?words=js'
const xhr=new XMLHttpRequest()
xhr.onreadystatechange=()=>{
if(xhr.readyState!==4)return;
if((xhr.status>=200&&xhr.status<300)||xhr.status==304){
console.log(xhr.response)
}
}
xhr.open("POST",url,true)
//准备之后,发送之前
//响应类型
//xhr.responseType=""//默认为空
//请求超时时间
xhr.timeout=10000
//解决跨域携带Cookie
xhr.withCredentials=true
//设置请求头信息
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded")
xhr.send("username=alen&age=18")
xhr.setRequestHeader("Content-Type","application/json")//
xhr.send(JSON.stringify({
username:"lisi"
}))
</script>
4.3 XHR的事件
1.load事件【类似readystatechange监听事件】
响应数据可用后才会触发
IE6~8不支持load事件
<script>
const url='https://www.imooc.com/api/http/search/suggest?words=js'
const xhr=new XMLHttpRequest()
//监听事件
xhr.onload=()=>{
if((xhr.status>=200&&xhr.status<300)||xhr.status==304){
console.log(xhr.response)
}
}
//或者
xhr.addEventListener("load",()=>{
if((xhr.status>=200&&xhr.status<300)||xhr.status==304){
console.log(xhr.response)
}
})
xhr.open("POST",url,true)
//设置请求头信息
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded")
xhr.send("username=alen&age=18")
</script>
2.error事件【请求发送错误触发】
请求发送错误触发,比如:域名错误
IE10开始支持
<script>
const url='https://www.imooc.com/api/http/search/suggest?words=js'
const xhr=new XMLHttpRequest()
//监听事件
xhr.addEventListener("load",()=>{
if((xhr.status>=200&&xhr.status<300)||xhr.status==304){
console.log(xhr.response)
}
})
xhr.addEventListener("error",()=>{
console.log("error")
})
xhr.open("POST",url,true)
//设置请求头信息
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded")
xhr.send("username=alen&age=18")
</script>
3.abort事件【终止请求触发】
调用abort()终止请求时触发
IE10开始支持
<script>
const url='https://www.imooc.com/api/http/search/suggest?words=js'
const xhr=new XMLHttpRequest()
//监听事件
xhr.addEventListener("load",()=>{
if((xhr.status>=200&&xhr.status<300)||xhr.status==304){
console.log(xhr.response)
}
})
xhr.addEventListener("error",()=>{
console.log("error")
})
xhr.addEventListener("abort",()=>{
console.log("abort")
})
xhr.open("POST",url,true)
//设置请求头信息
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded")
xhr.send("username=alen&age=18")
//请求终止--触发abort事件
xhr.abort()
</script>
4.timeout事件【请求超时触发】
IE8开始支持
<script>
const url='https://www.imooc.com/api/http/search/suggest?words=js'
const xhr=new XMLHttpRequest()
//监听事件
xhr.addEventListener("load",()=>{
if((xhr.status>=200&&xhr.status<300)||xhr.status==304){
console.log(xhr.response)
}
})
xhr.addEventListener("error",()=>{
console.log("error")
})
xhr.addEventListener("abort",()=>{
console.log("abort")
})
xhr.addEventListener("timeout",()=>{
console.log("timeout")
})
xhr.open("POST",url,true)
//设置请求头信息
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded")
//设置请求超时事件
xhr.timeout=30000
//发送请求数据
xhr.send("username=alen&age=18")
</script>
5. Ajax进阶
5.1 FormData
1.使用Ajax提交表单
FormData可用于发送表单数据
<body>
<form
id="login"
action="https://www.imooc.com/api/http/search/suggest?words=js"
method="POST"
enctype="application/x-www-form-urlencoded"//常见形式
//enctype="multipart/form-data"//也是表单提交的一种
>
<input type="text" name="username" placeholder="用户名" />
<input type="password" name="password" placeholder="密码" />
<input type="submit" id="submit" value="登陆" />
</form>
<script>
//1.使用Ajax提交表单
const login = document.getElementById("login");
// console.log(login.username,login.password)
const { username, password } = login;
const btn=document.getElementById("submit")
const url="https://www.imooc.com/api/http/search/suggest?words=js"
btn.addEventListener("click",(e)=>{
//阻止表单默认提交
e.preventDefault()
//表单数据验证
//发送Ajax请求
const xhr=new XMLHttpRequest()
xhr.addEventListener("load",()=>{
if((xhr.status>=200&&xhr.status<300)||xhr.status===304){
console.log(xhr.response)
}
})
xhr.open("POST",url,true)
//组装一下数据
// const data=`username=${username.value}&password=password.value`
//formData用来封装form表单的数据
const data=new FormData(login)
// console.log(data)
// for(const item of data){
// console.log(item)
// }
// xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded")
xhr.send(data)
},false)
</script>
</body>
2.FormData的基本用法
通过HTML表单元素创建FormData对象
IE10开始支持
const fd=new FormData(表单元素)
//可以通过append()方法添加数据
fd.append(键,值)
//发送数据
xhr.send(fd)
5.2 封装Ajax
//index.html页面主要代码
<script type="module">
import {ajax,get,getJSON,post} from "./index.js"
const url="https://www.imooc.com/api/http/search/suggest?words=js"
const xhr=ajax(url,{
method:"POST",
params:{username:'lisi'},
data:{
age:19
},
responseType:"json",
success(response,xhr){
console.log(response)
},
httpCodeError(err,xhr){
console.log("Http code error:",err)
},
error(xhr){
console.log("error",xhr)
},
abort(xhr){
console.log("abort",xhr)
},
timeout(xhr){
console.log("timeout",xhr)
}
})
</script>
//index.js文件
import Ajax from "./ajax.js"
const ajax=(url,options)=>{
return new Ajax(url,options).getXHR()
}
const get=(url,options)=>{
return ajax(url,{...options,method:"GET"})
}
const getJSON=(url,options)=>{
return ajax(url,{...options,method:"GET",responseType:"json"})
}
const post=(url,options)=>{
return ajax(url,{...options,method:"POST"})
}
export {ajax,get,getJSON,post}
//ajax.js文件
//引入默认参数模块
import DEFAULTS from "./defaults.js"
//引入工具函数模块,处理url
import { serialize, serializeJSON, addURLData } from "./utils.js"
//引入常量模块
import { HTTP_GET, CONTENT_TYPE_FORM_URLENCODED, CONTENT_TYPE_JSON } from "./constants.js"
//Ajax类
class Ajax {
constructor(url, options) {
//url保存到this上,这样这个属性,这个类的其他地方也可以访问
this.url = url
this.options = Object.assign({}, DEFAULTS, options)
//初始化
this.init()
}
init() {
//创建xhr对象
const xhr = new XMLHttpRequest()
//希望别的方法也访问到它,所以这里添加到this上
this.xhr = xhr
//监听事件
this.bindEvents()
xhr.open(this.options.method, this.url + this.addParam(), true)
//设置responseType
this.setResponseType()
//设置跨域是否携带Cookie
this.setCookie()
//设置超时
this.setTimeout()
//发送请求
this.sendData()
}
//绑定响应事件处理程序
bindEvents() {
const xhr = this.xhr
//对象解构赋值
const { success, httpCodeError, error, abort, timeout } = this.options
//拿到响应数据后调用load事件
xhr.addEventListener("load", () => {
if (this.ok()) {
//响应成功
success(xhr.response, xhr)
} else {
httpCodeError(xhr.status, xhr)
}
}, false)
//请求出现出错时,触发error事件
xhr.addEventListener("error", () => {
error(xhr)
}, false)
//请求终止,触发abort事件
xhr.addEventListener("abort", () => {
abort(xhr)
}, false)
//请求超时,触发timeout事件
xhr.addEventListener("timeout", () => {
timeout(xhr)
}, false)
}
//检测响应的HTTP状态码是否正常
ok() {
const xhr = this.xhr
return (xhr.status >= 200 && xhr.status < 300) || xhr.status === 304
}
//在地址上添加数据
addParam() {
const { params } = this.options
if (!params) return "";
return addURLData(this.url, serialize(params))
}
//设置响应类型的函数
setResponseType() {
this.xhr.responseType = this.options.responseType
}
//设置跨域是否携带Cookie
setCookie() {
if (this.options.withCredentials) {
this.xhr.withCredentials = true
}
}
//设置超时
setTimeout() {
const { timeoutTime } = this.options
if (timeoutTime > 0) {
this.xhr.timeout = timeoutTime
}
}
//发送请求
sendData() {
const xhr = this.xhr
if (!this.isSendData()) {
return xhr.send(null)
}
//判断发送那种数据
let resultData = null
if (this.isFormData()) {
//发送FormData格式的数据
resultData = this.options.data
} else if (this.isFormURLEncodeData()) {
//设置Content-Type
this.setContentType(CONTENT_TYPE_FORM_URLENCODED)
//发送application/x-www-form-urlencoded格式的数据
resultData = serialize(this.options.data)
} else if (this.isJSONData()) {
//设置Content-Type
this.setContentType(CONTENT_TYPE_JSON)
resultData = serializeJSON(this.options.data)
}else{
this.setContentType()
//发送其他格式数据
resultData=this.options.data
}
xhr.send(resultData)
}
//是否使用send发送数据
isSendData() {
const { data, method } = this.options
if (!data) return false;
// if(method==='GET'||method==='get') return false;
if (method.toLowerCase() === HTTP_GET.toLowerCase()) return false;
return true
}
//是否使用FormData的数据
isFormData() {
// 判断是不是FormData的实例
return this.options.data instanceof FormData
}
//是否使用application/x-www-form-urlencoded格式的数据
isFormURLEncodeData() {
return this.options.contentType.toLowerCase().includes(CONTENT_TYPE_FORM_URLENCODED)
}
//是否使用application/json格式的数据
isJSONData() {
return this.options.contentType.toLowerCase().includes(CONTENT_TYPE_JSON)
}
//设置Content-Type
setContentType(contentType=this.options.contentType){
if(!contentType)return;
this.xhr.setRequestHeader("Content-Type",contentType)
}
//获取xhr对象
getXHR(){
return this.xhr
}
}
export default Ajax;
//utils.js文件
//工具函数
const serialize=param=>{
const results=[]
//遍历对象
for(const {key,value} of Object.entries(param)){
results.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
}
//['username=alen','age=18']
//将数组转换为字符串
return results.join("&")//'username=alen&age=18
}
//数据序列化成JSON格式的字符串
const serializeJSON=param=>{
return JSON.stringify(param)
}
//给URL添加参数
//url:www.baidu.com?name=18&考虑加?还是 &
const addURLData=(url,data)=>{
if(!data)return ""
const mark=url.includes("?")?"&":"?"
return `${mark}${data}`
}
//暴露出去
export {
serialize,
serializeJSON,
addURLData
}
//defaults.js文件
//引入常量模块
import { HTTP_GET,CONTENT_TYPE_FORM_URLENCODED } from "./constants.js"
//默认参数文件
export const DEFAULTS={
method:HTTP_GET,
//请求头携带的数据
params:null,
// params:{
// username:'alen',
// age:18
// },
//请求体携带数据
data:null,
// data:{
// username:"lisi",
// sex:"female"
// },
// data:FormData数据,
contentType:CONTENT_TYPE_FORM_URLENCODED,
responseType:"",
timeoutTime:0,
withCredentials:false,
//方法
success(){},
httpCodeError(){},
error(){},
abort(){},
timeout(){}
}
export default DEFAULTS
//constants.js文件
//常量模块文件
export const HTTP_GET="GET"
export const CONTENT_TYPE_FORM_URLENCODED="application/x-www-form-urlencoded"
export const CONTENT_TYPE_JSON="application/json"
5.3 使用Promise改造封装好的Ajax
//封装Ajax的代码如上再次基础上改动代码
//index.html文件
<script type="module">
import { ajax, get, getJSON, post } from "./index.js";
const url = "https://www.imooc.com/api/http/search/suggest?words=js";
const p = getJSON(url, {
params: { username: "lisi" },
data: { age: 19 },
// timeoutTime:10,
});
// p.xhr.abort()
const { ERROR_HTTP_CODE, ERROR_REQUEST, ERROR_TIMEOUT, ERROR_ABORT } = p;
p.then((response) => {
console.log(response);
}).catch((err) => {
// console.log(err);
switch(err.type){
case ERROR_HTTP_CODE:
console.log("HTTP状态码错误");
break;
case ERROR_REQUEST:
console.log("请求错误");
break;
case ERROR_TIMEOUT:
console.log("请求超时");
break;
case ERROR_ABORT:
console.log("请求终止");
break;
}
});
</script>
//index.js文件
import Ajax from "./3.ajax.js"
//引入常量
import {
ERROR_HTTP_CODE,
ERROR_HTTP_CODE_TEXT,
ERROR_REQUEST,
ERROR_REQUEST_TEXT,
ERROR_TIMEOUT,
ERROR_TIMEOUT_TEXT,
ERROR_ABORT,
ERROR_ABORT_TEXT
} from "./constants.js"
const ajax = (url, options) => {
let xhr
const p = new Promise((resolve, reject) => {
xhr = new Ajax(url, {
...options,
...{
success(response, xhr) {
resolve(response)
},
httpCodeError(status) {
reject({
type: ERROR_HTTP_CODE,
text: `${ERROR_HTTP_CODE_TEXT}:${status}`
})
},
error() {
reject({
type: ERROR_REQUEST,
text: ERROR_REQUEST_TEXT
})
},
abort(status) {
reject({
type: ERROR_ABORT,
text: ERROR_ABORT_TEXT
})
},
timeout(status) {
reject({
type: ERROR_TIMEOUT,
text: ERROR_TIMEOUT_TEXT
})
},
}
}).getXHR()
})
p.xhr;
p.ERROR_HTTP_CODE = ERROR_HTTP_CODE
p.ERROR_REQUEST = ERROR_REQUEST
p.ERROR_TIMEOUT = ERROR_TIMEOUT
p.ERROR_ABORT=ERROR_ABORT
return p;
}
const get = (url, options) => {
return ajax(url, { ...options, method: "GET" })
}
const getJSON = (url, options) => {
return ajax(url, { ...options, method: "GET", responseType: "json" })
}
const post = (url, options) => {
return ajax(url, { ...options, method: "POST" })
}
export { ajax, get, getJSON, post }
//constants.js文件
//常量模块文件
export const HTTP_GET="GET"
export const CONTENT_TYPE_FORM_URLENCODED="application/x-www-form-urlencoded"
export const CONTENT_TYPE_JSON="application/json"
export const ERROR_HTTP_CODE=1
export const ERROR_HTTP_CODE_TEXT="HTTP状态码异常"
export const ERROR_REQUEST=2
export const ERROR_REQUEST_TEXT="请求被阻止"
export const ERROR_TIMEOUT=3
export const ERROR_TIMEOUT_TEXT="请求超时"
export const ERROR_ABORT=4
export const ERROR_ABORT_TEXT="请求终止"
6. Ajax应用
6.1 搜索提示
//引入的还是上面封装的ajax代码,这里主要实现的是搜索所做的事情
<body>
<input type="text" id="search" />
<ul id="result"></ul>
<script type="module">
import { getJSON } from "../Promise改造ajax/index.js";
const searchInput = document.getElementById("search");
const resultList = document.getElementById("result");
const url = "https://www.imooc.com/api/http/search/suggest?words=";
//输入框输入触发发送请求事件
function handleInputEvent() {
if (searchInput.value.trim() !== "") {
getJSON(`${url}${searchInput.value}`)
.then((response) => {
const data = response.data;
let html = "";
for (const item of data) {
html += `<li>${item.word}</li>`;
}
resultList.innerHTML = html;
resultList.style.display = "";
})
.catch((err) => {
console.log(err);
});
} else {
resultList.innerHTML = "";
resultList.style.display = "none";
}
}
//防抖函数
const debounce = function (delay, callback) {
let timer;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
callback();
}, delay);
};
};
const debounceFn = debounce(1000, handleInputEvent);
searchInput.addEventListener("input", function () {
debounceFn()
});
//或者写成下面这种
// let timer
// searchInput.addEventListener("input", function () {
// if(timer){
// clearTimeout(timer)
// }
// timer=setTimeout(handleInputEvent,3000)
// });
</script>
</body>
6.2 二级菜单
6.3 多个Ajax请求的并发执行
//引入的还是上面封装的ajax代码,这里主要实现的是ajax的并发执行所做的事情
<script type="module">
import { getJSON } from "../Promise改造ajax/index.js";
const menuURL = "https://www.imooc.com/api/mall-PC/index/menu";
const adURL = "https://www.imooc.com/api/mall-PC/index/ad";
const loadingPageEl = document.getElementById("loading-page");
const adEl = document.getElementById("ad");
const menuEl = document.getElementById("menu");
const p1 = getJSON(menuURL)
.then((response) => {
console.log("菜单:", response);
}).catch((err) => {
console.log(err);
});
const p2 = getJSON(adURL)
.then((response) => {
// console.log("图片:", response);
let html = "";
for (const item of response.data) {
html += `<img src="${item.url}"></img>`;
}
adEl.innerHTML = html;
})
.catch((err) => {
console.log(err);
});
Promise.all([p1, p2]).then(() => {
// loadingPageEl.style.display="none"
//这里添加一个类来隐藏还没拿到数据的时候的页面
loadingPageEl.classList.add("none"); //IE10开始支持
});
</script>
7. Ajax扩展【axios,Fetch】
7.1 axios【返回是Promise对象】
1.axios是什么?
axios是一个基于Promise的HTTP库,可以用在浏览器和nodejs中;第三方Ajax库
推荐一个axios中文官方网址:www.axios-js.com/zh-cn/docs/
2.axios的基本用法
1.安装
npm install axios
使用cdn:
2.axios的使用方式
axios():第一个参数是url地址,第二个参数传的是配置参数
axios.get():第一个参数是url地址,第二个参数传的是配置参数
axios.post():第一个参数是url地址,第二个参数是要传的数据
axios.put()
axios.delete()
//1.引入axios
<script src="https://unpkg.com/axios@0.27.2/dist/axios.min.js"></script>
//2.使用
<script>
const url="https://www.imooc.com/api/http/search/suggest?words=js"
axios(url,{
method:"post",//请求方式
headers:{//请求头信息
"Content-Type":"application/x-www-form-urlencoded",
// "Content-Type":"application/json"
},
params:{//请求头携带的数据
username:"lisi"
},
//"application/x-www-form-urlencoded"
data:"age=18&sex=male",
//"application/json"
// data:{//请求体携带数据
// age:19,
// sex:"male"
// }
timeout:3000,//请求超时时间
// withCredentials:true//跨域请求是否携带Cookie
}).then(response=>{
console.log(response.data)
}).catch(err=>{
console.log(err)
})
</script>
//便捷方式
//get第二个参数传配置相关信息的
axios.get(url,{
params:{
username:'alen'
}
}).then(response=>{
console.log(response.data)
}).catch(err=>{
console.log(err)
})
//post的第二个参数直接传数据
// 字符串
axios.post(url,"username=lis&age=19").then(response=>{
console.log(response.data)
}).catch(err=>{
console.log(err)
})
//json格式
axios.post("https://www.imooc.com/api/http/json/search/suggest?words=js",{
username:'alen'
}).then(response=>{
console.log(response.data)
}).catch(err=>{
console.log(err)
})
//axios.put()
//axios.delete()
7.2 Fetch【返回是Promise对象】
1.Fetch是什么
Fetch也是前后端通信的一种方式;Fetch是Ajax(XMLHttpRequest)的一种替代方案,它是基于Promise的
Ajax的兼容性比Fetch好;Fetch没abort(),timeout()
2.Fetch的基本用法
fetch()第一个参数是请求地址的url,第二个参数是对象配置fetch
const url="https://www.imooc.com/api/http/search/suggest?words=js"
//FormData格式
const fd=new FormData()
fd.append("username",'haha')
fetch(url,{
method:"post",
//请求体携带数据
// body:null,
// 字符串格式
// body:"username=lisi&age=19",
// headers:{
// "COntent-Type":"application/x-www-form-urlencoded"
// },
//JSON格式
// body:JSON.stringify({username:"zs"}),
// headers:{
// "COntent-Type":"application/json"
// }
//FormData格式
body:fd,
//跨域资源共享
mode:"cors",
credentials:"include"//跨域请求是否携带Cookie
})
fetch()调用之后返回Promise对象
body/bodyUsed:body只能读一次,读过之后不让在读了
ok:如果为true,表示可以读取数据,不用在去判断HTTP状态码了
response.json():返回值也是一个Promise对象
<script>
console.log(fetch)
const url="https://www.imooc.com/api/http/search/suggest?words=js"
//FormData格式
const fd=new FormData()
fd.append("username",'haha')
fetch(url,{
method:"post",
//请求体携带数据
// body:null,
// 字符串格式
// body:"username=lisi&age=19",
// headers:{
// "COntent-Type":"application/x-www-form-urlencoded"
// },
//JSON格式
// body:JSON.stringify({username:"zs"}),
// headers:{
// "COntent-Type":"application/json"
// }
//FormData格式
body:fd,
//跨域资源共享
mode:"cors",
credentials:"include"//跨域请求是否携带Cookie
}).then(res=>{
console.log(response)
//body/bodyUsed
//body只能读一次,读过之后不让在读了
//ok
if(response.ok){
// console.log(res.json())
//json格式
return response.json()//返回给下一个then()接收数据
// 文本格式
// return response.text
}else{
throw new Error(`HTTP CODE 异常${response.status}`)
}
}).then(data=>{
console.log(data)
})
.catch(err=>{
console.log(err)
})
</script>
\