目录
1.XMLHttpRequest
get请求
ajax.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>ajax 演示</title>
</head>
<body>
<p>一段文字 1</p>
<script src="./ajax.js"></script>
</body>
</html>
ajax.js
const xhr = new XMLHttpRequest()
xhr.open('GET', '/data/test.json', true) // true为异步,false为同步
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(JSON.parse(xhr.responseText))
alert(xhr.responseText)
} else {
console.log('其他情况')
}
}
}
xhr.send(null)
test.json
{
"name": "zhangsan"
}
运行结果
流程和api说明
get请求的send方法可以传null也可以不传,我估摸着源码判断就是 == null,这样同时判断undefined和null
GET 还是 POST?
与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用。
然而,在以下情况中,请使用 POST 请求:
- 无法使用缓存文件(更新服务器上的文件或数据库)
- 向服务器发送大量数据(POST 没有数据量限制)
- 发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠
如需使用同步 async=false,将 open() 方法中的第三个参数改为 false,不推荐使用 async=false,但是对于一些小型的请求,也是可以的。总之,都写为true是没问题的。注释:当使用 async=false 时,不要编写 onreadystatechange 函数 - 把准备写的逻辑放到 send() 语句后面即可(但没有谁会这样用)
xhr.status就是http协议的状态,如下图
当请求被发送到服务器时,我们需要执行一些基于响应的任务。每当 readyState 改变时,就会触发 onreadystatechange 事件(这是一个回调函数)。readyState 属性存有 XMLHttpRequest 的状态信息。
xhr.readyState会从0到4变化
0 —— (未初始化)还没有调用send()方法
1 —— (载入)已调用send()方法,服务器连接已建立,正在发送请求
2 —— (载入完成) send()方法执行完成,已经接收到全部响应内容
3 —— (交互)正在解析响应内容
4 —— (完成)响应内容解析完成,可以在客户端调用
说这个xhr.readyState会从0到4变化有啥用?这就涉及一个调试技巧,如果发现onreadystatechange回调里面的逻辑没执行,那就去onreadystatechange里面写上console.log(xhr.readyState),会经历01234这5种情况,但是打印只会2 3 4的情况,因为1才与服务器刚建立接连,请求没发送完。如果没打印就对应看问题,单词拼错了?send忘了写??
post请求
给一个示范
const xhr = new XMLHttpRequest()
xhr.open('POST', '/login', true)
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
// console.log(JSON.parse(xhr.responseText))
alert(xhr.responseText)
} else {
console.log('其他情况')
}
}
}
const postData = {
username: 'zhangsan',
password: 'xxx'
}
xhr.send(JSON.stringify(postData))
2.同源策略和跨域
同源策略
什么是同源策略?
ajax请求时,浏览器要求和当前网页的server必须同源(安全)
什么是同源?
同源:协议、域名、端口,三者必须一致
比如前端http://a.com:8080/
server是https://b.com/api/xxx
比如这个前端网页上发ajax去请求server页面的数据,浏览器肯定会截获,不符合同源,不管是get还是post都不会让你发
服务端server不一样,没有同源策略,可以去获取其他的数据
可以不遵守同源策略的情况:
加载图片img、css、js可以无视同源策略
<img src=跨域的图片地址>
<link href=跨域的css地址>
<script src=跨域的js地址></script>
注意了,别人的图片可能做防盗链,是服务器做的,比如微信公众号文章的图做了防盗链的限制,如果不是自己域名的网站引用,可能说“此图片来自微信公众平台,未经允许不可引用”
比如这个,你把微信公众号文章的图复制粘贴在另一个论坛之类的,就会这样
<img />可用于统计打点,可使用第三方统计服务
<link /> <script>可使用CDN,CDN一般都是外域
<script>可实现JSONP
跨域
所有的跨域都必须经过server端允许、server端配合,未经server端允许就实现跨域,说明浏览器有漏洞,这是危险信号
3.jsonp和cors
引出问题
访问https://imooc.com/,服务端一定返回一个html文件吗?
不是,如果这样就是静态文件了,不会动态更新,服务器可以任意动态拼接数据返回,只要符合html格式要求。
同理:<script src="http://imooc.com/getData.js">就一定返回js文件吗?
不一定,都是url网址,不管是啥,服务器都会拼接任何满足条件的数据返回给你。这里服务器只需要拼接的东西符合js格式不报错就可以了。
jsonp的基本实现原理
我们知道,<script>可以绕过跨域限制,服务器可以任意动态拼接数据返回,所以<script>就可以获得跨域的数据,只要服务端愿意返回。jsonp就是这个原理。
来个代码演示:
jsonp.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>jsonp 演示</title>
</head>
<body>
<p>一段文字 1</p>
<script>
window.abc = function (data) {
console.log(data)
}
</script>
<!-- 实际操作可以写函数把下面的script动态插入 -->
<script src="http://localhost:8002/jsonp.js?username=xxx&callback=abc"></script>
</body>
</html>
<script src="http://localhost:8002/jsonp.js?username=xxx&callback=abc">
这里本地演示的时候换8002端口只是为了演示跨域,运行这个html的时候在8001端口
jsonp.js(假设这里是服务器动态拼接的数据name:'xxx',意思是执行abc函数,传参{ name : 'xxx' })
abc(
{ name: 'xxx' }
)
运行结果:
注意,这里必须在jsonp.js的目录开启8002端口的服务器,否则net::ERR_CONNECTION_REFUSED,如果开启服务器位置不对就会404
你真的了解jsonp的请求过程吗(例子均为gif演示)
实际操作没必要定义window.abc=....也没必要引入<script>,只要服务端配合,前端可以定义函数或者工具封装起来来实现跨域的过程
jQuery演示jsonp
当然,我再举个例子吧,我自己用代码试了一下,来看看jQuery的ajax请求例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<script>
$(function(){
$.ajax({
url:'http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1',
dataType: 'jsonp',
crossDomain:true, // 不写这个无法跨域
jsonCallback: 'abc', // 服务端一定会执行回调callback,但是我这里想重命名,但是有用吗??????????
success: function(data) { // 这里就是要执行的回调函数
console.log(data)
}
})
});
</script>
</body>
</html>
运行结果看看
只要在url中没手动写callback=xxxx,那就算写了jsonCallback也没用,那如果是jsonp参数呢,写jsonp:'xxxx'参数就相当于在url手动加上xxxx=?没手动写callback=?后面还是随机的
$.ajax({
url:'http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1',
dataType: 'jsonp',
crossDomain:true,
jsonp: 'xxxx', // 新加的属性
jsonCallback: 'abc',
success: function(data) {
console.log(data)
}
})
如果写了jsonp参数呢?我们来看看
其实完全可以不写jsonCallback,只需要知道一定会执行对应回调就行了
看这里,我手动加了callback=1233,没用的大兄弟,我们来看看
$.ajax({
url:'http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1&callback=1233', //让服务端将callback改名为abc
dataType: 'jsonp',
crossDomain:true,
success: function(data) { // 这里就是回调的abc函数
console.log(data)
}
})
运行结果,可以发现2个callback,后面的读取随机callback会覆盖前面我们设置的1233,所以服务器最后返回的还是浏览器随机产生的,jQuery里面就是这样,但是vue的jsonp可就不一样了,写啥就是啥,后面我们来看看vue的jsonp
vue演示jsonp
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://unpkg.com/vue"></script>
<!-- 注意:vue-resource 依赖于 Vue,所以先后顺序要注意 -->
<!-- this.$http.jsonp -->
<script src="https://unpkg.com/vue-resource"></script>
</head>
<body>
<div id="app">
<input type="button" value="jsonp请求" @click="jsonpInfo">
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {
jsonpInfo() { // 发起JSONP 请求
this.$http.jsonp('http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1',
{jsonpCallback:"lcytestname"}
).then(result => {
console.log(result.body)
})
}
}
});
</script>
</body>
</html>
一般可以这样设置
this.$http.jsonp(url,{params:params,jsonp: 'callBackParam',jsonpCallback: "getCallBack"})
jsonp设置的是传callback的参数名,就是jsonp:xxx,则到时候请求就是xxx=?而不是callback=?
jsonpCallback是返回的函数名
vue是自动处理跨域,这完全没问题,不用另外设置什么crossDomain属性
运行结果
上面例子我都测试过了,大家可以直接复制自行测试学习!找例子不容易~~~
cors
服务器设置http header,大家感兴趣可以自行搜索
4.练习题
1.手写一个简易的ajax
简单原理如下图,接着我们写一下完整点的代码
function ajax(url) {
const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(JSON.parse(xhr.responseText));
} else if (xhr.status === 404) { // 如果需要更多情况可自行添加判断
reject(new Error("404 not found"));
}
}
};
xhr.send(null)
})
return p;
}
const url = '/data/test.json'
ajax(url).then(res => console.log(res))
.catch(err => console.log(err));
更多的却不一定让你去手写一个ajax,因为ajax已经比较完善了,手写这些东西是为了了解原理,框架都是封装,原理类似,axios也是利用XMLHttpRequest封装的
关注、留言,我们一起学习。
===============Talk is cheap, show me the code================