Ajax
Ajax 即“Asyn Javascript And XML”(异步 JavaScript 和 XML),是指一种创建交互式、快速动态网页应用的网页开发技术,无需重新加载整个网页的情况下,能够更新部分网页的技术。
通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新,所以也被称之为 局部刷新的更新技术
猫眼电影接口文档:
i.maoyan.com/api/mmdb/mo…
Ajax基础
整体流程
一. 创建一个XMLHttpRequest对象
二. 调用(有三个参数)
- 请求方式
- get(会把你提交的的东西都暴露在地址上)
- post不会,它放在请求体里,所以更安全一点
- 请求地址(文件在哪)
- 传布尔值(是否异步,true是异步,false为同步)
三. 发出去
四. 监听readyState值(或者直接用onload)
- 监听完readyState值,继续监听状态码
<script>
//一. 创建一个XMLHttpRequest对象
var xhr=new XMLHttpRequest()
// 二. 调用(有三个参数)
// 1. 请求方式
// 2. 请求地址(文件在哪)
// 3. 传布尔值(是否异步,true是异步,false为同步)
xhr.open("get","./1.json",true)
// 三. 发出去
xhr.send()
</script>
我们可以认为,此时已经跟后端联系上了,并且把数据拿回来了,只不过是 调试工具拿到了 ,我们还没有拿到
所以再进行监听
//监听
xhr.onreadystatechange=function(){
console.log('电话接通')
}
为什么是三次?
因为👇:
也就是说👇:其实这个监听,监听的是readyState的改变,只有等于4时,可以拿到成功结果
所以,我们现在监听readyState的改变
xhr.onreadystatechange=function(){
if(xhr.readyState===4){
console.log(xhr.responseText)
}
}
成功拿到
但是你并不确定你上面有没有出错,(比如第二步请求的时候),所以我们再判断一下状态码
(如果正确在二百到三百之间,就解析转一下格式;不然就返回错误)
xhr.onreadystatechange=function(){
if(xhr.readyState===4){
if(xhr.status===200){
// 返回的是json字符串格式
console.log(JSON.parse(xhr.responseText))
}else{
console.log("error",xhr.responseText)
}
}
}
比如我们渲染一个页面👇
<button id="btn">aaa</button>
<ul id="list">
</ul>
</body>
<script>
var obtn=document.querySelector("#btn")
var olist=document.querySelector("#list")
//——————————按钮里发请求
obtn.onclick=function(){
//创建一个XMLHttpRequest对象
var xhr=new XMLHttpRequest()
// 调用(有三个参数)
xhr.open("get","./1.json",true)
// 发出去
xhr.send()
// 监听
xhr.onload=function(){
if(/^2\d{2}$/.test(xhr.status)){
// 返回的是json字符串格式
//console.log(JSON.parse(xhr.responseText))
//调用渲染页面
render(JSON.parse(xhr.responseText))
}else{
console.log("error",xhr.responseText)
}
}
//————————————————
// 渲染函数
function render(res){
var newlist = res.data.list.map(function(item){
return `<li>
<img src="${item.imgeUrl}"/>
<div>${item.name}</div>
</li>`
})
//数组转成字符串会用逗号隔开,所以我们需要把逗号去掉
olist.innerHTML=newlist.join("")
}
}
</script>
Ajax请求方式
json-server
// 1.json
{
"list":[],
"users":[],
"shopcar":[],
"detail":{
"name":"手机"
}
}
GET
比如
xhr.open("get","地址/users?id=1",true)
POST
POST在send里发送,也就是在send里写
POST两种发送格式👇
form编码—— name=kerwin&age=18
json—— {name:"kerwin",age=100}
————————————————————————————————
但是后端并不知道你传来的到底是form还是json,所以在使用post发请求时,需要在send前加一句
如果是form编码,就写👇
xhr.setRequestHeader("content-type","application/x-www-form-urlencoded")
xhr.send(`name=tiechui&age=18`)
如果是json,就写👇
xhr.setRequestHeader("content-type","application/json")
//对一个json对象,转成json字符串
xhr.send(JSON.stringify({
name:"winter"
age:90
}))
PUT(全部覆盖式更新)
必须先在地址里说明更新的是哪个数据(把/id写后面)
同时它也支持form和json方法,所以send前面那句也要写
然后在send里写,但是注意这个PUT是全量更新
PATCH(部分更新,补丁式)
必须先在地址里说明更新的是哪个数据(把/id写后面)
同时它也支持form和json方法,所以send前面那句也要写
然后在send里写,可以只写需要更新的
delete
只需要在地址处告诉它要删的是哪个,把/id传过去
fetch基础
基于promise实现
为了取代 XHR 的一种方式,还是属于Ajax的实现方式之一
GET
fetch默认就是get请求
fetch("http://localhost:3000/users")
这个返回的是一个promise对象
后面接两个.then()
这个.then里面可以指定一个回调函数,可以调用原型prototype上的两个方法(.json和.text,下面用.json举例)
第一个.then,需要调用.json,把我们的东西转换成json的数据
然后return出去,在后面就能拿到了
————————————
第二个.then,拿到的就是前面.then的返回值
其实就是.then的链式调用,后面.then拿到前面.then的返回值
完整就是
// .json
fetch("http://localhost:3000/users")
.then(res=>res.json())
.then(res=>{
console.log(res)
})
// .text
fetch("http://localhost:3000/users")
.then(res=>res.text())
.then(res=>{
console.log(JSON.parse(res))
})
get传参直接放在地址上?传就行
POST
<body>
<button id="btn">aaa</button>
</body>
<script>
var obtn=document.querySelector("#btn")
obtn.onclick=function(){
fetch("http://localhost:3000/users",{
method:"POST",
headers:{
"content-type":"application/x-www-form-urlencoded"
},
body:"name=tiechui&age=18"
})
.then(res=>res.json())
.then(res=>{
console.log(res)
})
}
</script>
PUT、PATCH、DELETE
同理
fetch错误处理方案
fetch("http://localhost:3000/users")
.then(res=>{
if(res.ok){
return res.json()
}else{
//console.log(res)
return Promise.reject({
status:res.status,
statusText:res.statusText
})
}
})
.then(res=>{
console.log(res)
}).catch(err=>{
console.log("err",err)
})
async和await 与fetch异步的结合,可以让上面的异步代码类似于同步(看起来像——执行完这个,才能下一步)
异步代码写起来像同步一样,异步编程的最高境界
axios基础
axios是一个基于promise的HTTP库,可以用在浏览器和node.js中
//引入
// 写在最上面,里面什么也不写
<script src="https://cdn.jsdelivr.net/npm/axios@1.1.2/dist/axios.min.js"></script>
GET
<body>
<button id="btn">aaa</button>
</body>
<script>
var obtn=document.querySelector("#btn")
obtn.onclick=function(){
// get传参方式
//1. 直接地址?后面写
//2. 如果要传对象,可以用params(它最后还是帮你组装进地址里)
axios.get("http://localhost:3000/users",{
params:{
name:"kerwin"
}
})
.then(res=>{
console.log(res.data)
})
.catch(err=>{
console.log("err",err)
})
}
</script>
POST
json字符串格式
obtn.onclick=function(){
axios.post("http://localhost:3000/users",{
name:"kerwin",
age:18
})
.then(res=>{
console.log(res.data)
})
.catch(err=>{
console.log("err",err)
})
}
form
obtn.onclick=function(){
axios.post("http://localhost:3000/users","name=tiechui&age=80")
.then(res=>{
console.log(res.data)
})
.catch(err=>{
console.log("err",err)
})
}
这样可以把json对象转成form格式👇
const params = new URLSearchParams({name:"phy",age:18});
axios.post("http://localhost:3000/users",params)
……PUT、PATCH、DELETE
完整版axios配置写法
obtn.onclick=function(){
axios({
method:'POST',
url:'http://localhost:3000/shopcar',
//get params
//put post data
data:{
name:'phy',
age:18
}
}).then(res=>{
console.log(res.data)
}).catch(err=>{
console.log("err",err)
})
}
axios拦截器(config)
请求拦截器request(前置)
响应拦截器response(后置)
两者结合loading使用
<script>
var obtn = document.querySelector("#btn")
//放外面,对整个页面生效
// !!! 请求拦截器
axios.interceptors.request.use(function (config) {
//在请求被发出之前做一些事情
console.log("loading框显示....")
return config;
}, function (error) {
return Promise.reject(error);
});
// !!! 响应拦截器
axios.interceptors.response.use(function (response) {
console.log("成功隐藏loading...")
return response;
//也可以往里面放一些自己写的
// return {
// ...response,
// myname:'kerwin'
// }
}, function (error) {
console.log("失败隐藏loading...")
return Promise.reject(error);
});
obtn.onclick = function () {
axios.get("http://localhost:3000/users", {
params: {
name: "phy"
}
})
.then(res => {
console.log(res.data)
})
.catch(err => {
console.log("err", err)
})
}
</script>
效果👇
axios中断器
性能方面考虑
如果你点一个a,然后又快速点b;这个时候a还没回来,然后我们把它的请求停掉,继续请求b
语法👇
const controller = new AbortController();
axios.get('/foo/bar', {
signal: controller.signal
}).then(function(response) {
//...
});
// cancel the request
controller.abort()
具体使用👇
<button id="btn">点一下</button>
<button id="abort">abort</button>
const controller = new AbortController();
obtn.onclick = function () {
axios.get("http://localhost:3000/users", {
signal: controller.signal
})
.then(res => {
console.log(res.data)
})
.catch(err => {
console.log("err", err)
})
}
//终止
oabort.onclick=function(){
controller.abort()
}
同源策略
一般URL有三部分组成:协议http、域名(指向主机)www、端口号,只有这三个完全相同的URL才能称之为同源。
——是一种保护策略
http://默认端口号是80
- 不符合的话
- 无法读取非同源网页的Cookie、LocalStorage
- 无法接触非同源网页的DOM
- 无法向非同源地址发送ajax请求(可以发送,但浏览器会拒绝接收响应)
只要不符合同源策略,我们就称之为 “跨域访问” ,浏览器一般是不允许的,下面会讲解决方法👇
解决跨域—jsonp
jsonp(JSON with Padding) 是json一种“使用模式”,可以让网页从别的域名(网站)那获取资料,即跨域读取数据
- 它本身是js
- 缺点:jsonp只能做get请求
为什么script能解决跨域问题
- 因为script标签本身是没有跨域限制的
- 需要后端配合,返回的是函数小括号()调用的格式
- 前端提前声明好这个函数
//1. 动态创建一个script标签
//2. 让src指向接口
//3. 然后再将它插到页面中
//4. 配合函数使用
//....
<script>
function test(){
console.log("111",data)
}
var oscript=document.createElement("script")
oscript.src="1.txt"
document.body.appendChild(oscript)
</script>
// 1.txt 中
test(“kerwin”)
为了避免前后端对接的函数名写的不一样,后端会允许前端在这个jsonp接口的后面传一个参数
这个参数就是前端提前声明好的这个函数名
百度搜索案例
百度搜索的一个jsonp接口
在wd这里传入你想搜索的字符(wd在src这个api链接里)
wd=${osearch.value}
这样就可以拿到与输入内容相关的数据了
这时你会发现,随着你jsonp的创建,你的script标签会越创越多(所以我们过河拆桥,把它移除掉👇)
// 等下载完执行了,就把这个标签移除掉
// 因为script标签已经加载到内存中运行了,所以即使删了,代码也能正常运行
oscript.onload = function(){
oscript.remove()
}
完整代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" name="" id="search">
<ul id="list">
</ul>
<script>
var osearch = document.querySelector('#search')
var olist = document.querySelector('#list')
osearch.oninput = function () {
// 拿取输入框值
console.log(osearch.value)
//避免输入框为空时报错,如果是空,就返回,不查了
if(osearch.value===""){
olist.innerHTML=""
return
}
var oscript = document.createElement("script")
oscript.src = `https://www.baidu.com/sugrec?pre=1&p=3&ie=utf-8&json=1&prod=pc&from=pc_web&sugsid=40171,39661,40206,40212,40216,40224,40274,40294,40291,40287,40285,40317,40080,40364,40351,40373,40367,40406&wd=${osearch.value}&his=%5B%7B%22time%22%3A1705912173%2C%22kw%22%3A%22%E6%88%AA%E5%9B%BE%E5%BF%AB%E6%8D%B7%E9%94%AE%22%7D%2C%7B%22time%22%3A1705947520%2C%22kw%22%3A%22%E7%BF%BB%E8%AF%91%22%2C%22fq%22%3A2%7D%2C%7B%22time%22%3A1706083906%2C%22kw%22%3A%22%E4%B8%80%E4%B8%AA%E5%90%AB%E6%9C%89%E5%A4%A7%E9%87%8F%E8%8B%B1%E6%96%87%E5%8D%95%E8%AF%8D%E7%9A%84csv%E6%96%87%E4%BB%B6%22%7D%2C%7B%22time%22%3A1706407752%2C%22kw%22%3A%22%E4%BC%98%E9%85%B7%22%2C%22fq%22%3A2%7D%2C%7B%22time%22%3A1706526327%2C%22kw%22%3A%22%E7%A8%80%E5%9C%9F%E6%8E%98%E9%87%91%E6%80%8E%E4%B9%88%E5%88%9B%E5%BB%BA%E4%B8%93%E6%A0%8F%22%7D%2C%7B%22time%22%3A1707051724%2C%22kw%22%3A%22octave%E7%9A%84disp%22%7D%2C%7B%22time%22%3A1707137166%2C%22kw%22%3A%22%E7%99%BE%E5%BA%A6%E7%83%AD%E6%90%9C%22%2C%22fq%22%3A3%7D%2C%7B%22time%22%3A1707137327%2C%22kw%22%3A%22%E6%9D%83%E9%87%8D%E7%9F%A9%E9%98%B5%E7%AC%A6%E5%8F%B7%22%7D%2C%7B%22time%22%3A1709272959%2C%22kw%22%3A%22%E6%89%87%E8%B4%9D%E8%8B%B1%E8%AF%AD%E7%BD%91%E9%A1%B5%E7%89%88%22%7D%2C%7B%22time%22%3A1709702586%2C%22kw%22%3A%22%E5%A4%A7%E9%BA%A6%22%7D%5D&req=2&csor=1&cb=test&_=1710147004051`
document.body.appendChild(oscript)
// 等下载完执行了,就把这个标签移除掉
oscript.onload = function(){
oscript.remove()
}
}
// 前端提前预留好的test
function test(data){
//console.log(data.g)
//.g就是这个数组
olist.innerHTML=data.g.map(item=>
`
<li>${item.q}</li>
`
).join("")
}
</script>
</body>
</html>
其他
2. 通过响应头解决跨域
响应头 中有这个——意思是允许所有的域通过(这个是后端设置的)
虽然这样看的话前端是可以直接跨域使用的
但是如果后端的 请求头 里设置了字段,就不能直接跨域使用了,必须前端在axios里的 headers 里传上这个字段(不同网站可能需要传的不一样)
像这样👇
3. 通过“反向代理方案”解决跨域
使用
nginx 反向代理服务器(在英文目录下解压)
原文链接:https://juejin.cn/post/7345356537982418978 作者:phy_winter