AJAX
一、基本理论
1、定义
AJAX 是一种从网页访问 Web 服务器的异步无刷新技术。
- 异步:同步是必须等待请求返回结果之后,才能继续其他操作。异步是可以在等待的时间里去继续其他操作,当响应就绪后对响应进行处理。
- 无刷新:按需加载。(当点击查看更多的时候,就会结合DOM向服务器请求将数据追加进去,并没有刷新页面)
- 无刷新场景:如果要让用户留在当前页面中,同时发出新的HTTP请求,就必须用JavaScript发送这个新请求,接收到数据后,再用JavaScript更新页面,这样一来,用户就感觉自己仍然停留在当前页面,但是数据却可以不断地更新。
AJAX (异步 JavaScript 和 XML)仅仅组合了:
- 浏览器内建的 XMLHttpRequest 对象(从 web 服务器请求数据)
- JavaScript 和 HTML DOM(显示或使用数据)
AJAX的缺点:
- 没有浏览历史,不能回退
- 存在跨域问题
- SEO不友好,因为所有的信息不是全部加载的,所以爬虫爬不到。
2、工作原理
3、异步编程
(1)与同步编程的区别:
同步按你的代码顺序执行,异步不按照代码顺序执行,异步的执行效率更高。
(2)使用异步编程的场景:
我们利用主线程完成简短、快速的操作,利用子线程完成消耗时间长的事情。因为子线程独立于主线程,所以即便出现阻塞也不会影响主线程的运行。
**子线程的局限:**一旦发射了以后就会与主线程失去同步,我们无法确定它的结束,如果结束之后需要处理一些事情,比如处理来自服务器的信息,我们是无法将它合并到主线程中去的。
解决局限:JS中的异步操作函数通过回调函数来实现异步任务的结果处理。
(3)回调函数
作用:它是在我们启动一个异步任务的时候告诉他:等他完成了这个任务之后要干什么。这样一来主线程几乎不用关心异步任务的状态了,他自己会善始善终。
function print() {
document.getElementById("demo").innerHTML="RUNOOB!";
}
// 第一个参数是回调函数。这个函数执行之后会产生一个子线程,子线程等待3秒,然后调用回调函数print,在命令行输出 "RUNOOB!"。在 setTimeout 函数执行之后主线程并没有停止
setTimeout(print, 3000);
// 简洁写法
setTmeout(function(){
document.getElementById("demo").innerHTML="RUNOOB!";
},3000);
二、XML和JSON
1、XML和HTML的区别
- XML(可扩展标记语言)是用来传输和存储数据。服务器将响应的结果以XML的形式返回给客服端。XML的标签是自定义的,用来表示一些数据。
- HTML是用来在网页中呈现数据的。HTML都是预定义标签。
2、JSON
之前进行数据交换时使用的格式是XML,服务器端给客服端返回结果的时候都是XML的形式。前端的JS接受到这个结果之后,就会对这个XML文件进行解析。
现在使用的是JSON。
// xml
<student>
<name>小敏</name>
<age>18</age>
</student>
// JSON
{"name":"小明","age":18}
三、HTTP
超文本传输协议(HTTP)的设计目的是保证客户端与服务器之间的通信。客户端(浏览器)向服务器提交 HTTP 请求;服务器向客户端返回响应。响应包含关于请求的状态信息以及可能被请求的内容。
1、常用方法GET 和 POST
在客户机和服务器之间进行请求-响应时,两种最常被用到的方法是:GET 和 POST。
- GET - 从指定的资源请求数据。查询字符串(名称/值对)是在 GET 请求的 URL 中发送的:/test/demo_form.php**?name1=value1&name2=value2**
- POST - 向指定的资源提交要被处理的数据。查询字符串(名称/值对)是在 POST 请求的 HTTP 消息主体中发送的:
2、请求报文 – 浏览器向服务器请求
格式和参数:
-
请求行 (请求类型-- POST GET) (url路径) (HTTP版本)
-
请求头 HOST:
Cookie:
Content-type:
User-Agent:
-
空行
-
请求体 uesrname=admin&&password=admin
每次请求的时候,是将以上四个部分进行拼接发送给服务端。
3、响应报文 – 服务器响应
格式和参数:
-
响应行 (HTTP版本) (响应状态码 – 200) (响应字符串 – ok)
-
响应头 HOST:
Cookie:
-
空行
-
响应体 – 里面是HTML内容
四、AJAX原理与实现
1、XMLHttpRequest 对象 — 与服务器交换数据
这个对象用于和服务器交换数据。这意味着可以更新网页的部分,而不需要重新加载整个页面。如果要将请求发送给服务器,需要利用对象的open()和send()方法。
(1)创建XHR对象
// 现代的(IE7+、Firefox、Chrome、Safari 以及 Opera)浏览器 -- 新建XMLHttpRequest对象
var request = new XMLHttpRequest();
// 老版本的 Internet Explorer (IE5 和 IE6) -- 新建ActiveXObject对象
var request = new ActiveXObject('Microsoft.XMLHTTP');
// 新旧版本混写:通过检测window对象是否有XMLHttpRequest属性来确定浏览器是否支持标准的XMLHttpRequest
var request;
if(window.XMLHttpRequest){
request = new XMLHttpRequest();
}else{
request = new ActiveXObject('Microsoft.XMLHTTP');
}
(2)XHR对象将请求发送到服务器 – XHR方法
a. open(method,url,async) 规定请求的类型、URL 以及是否异步处理请求:
- 第一个参数指定是GET还是POST,第二个参数是文件在服务器上的位置,第三个参数指定是否使用异步,默认是true异步,所以不用写。
- url – 服务器上文件的地址,该文件可以是任何类型的文件,比如 .txt 和 .xml,或者服务器脚本文件,比如 .asp 和 .php (在传回响应之前,能够在服务器上执行任务)。
- XMLHttpRequest 对象如果要用于 AJAX 的话,其 open() 方法的 async 参数必须设置为 true
request.open('GET', '/api/categories');
b. send()将请求发送到服务器
// GET请求不需要参数,POST请求需要把body部分以字符串string或者FormData对象传进去。
request.send();
c. GET 和 POST请求
一般情况下使用GET请求,因为它简单并且快速。但是以下情况会使用POST请求:
1、不使用缓存文件,需要更新服务器上的文件或者数据库
2、由于POST没有数据量的限制,所以向服务器发送大量数据的时候使用
3、POST更加稳定,所以发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠
// 使用GET请求的时候,由于得到的是缓存的数据,所以可以向URL添加一个唯一的ID
xmlhttp.open("GET","/try/ajax/demo_get.php?t=" + Math.random(),true);
xmlhttp.send();
// 通过 GET 方法发送信息,请向 URL 添加信息:
xmlhttp.open("GET","/try/ajax/demo_get2.php?fname=Henry&lname=Ford",true);
xmlhttp.send();
2、XHR响应 – XHR属性
如果要获得来自服务器的响应,那么就要使用XMLHttpRequest 对象的 responseText (获得字符串形式的响应数据)或 responseXML(获得 XML 形式的响应数据。)属性。
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
3、onreadystatechange 事件 – XHR属性
当请求被发送到服务器,会根据服务器的处理状态readyState触发一些 onreadystatechange事件,执行一些基于响应的任务。以下是XMLHttpRequest 对象的三个重要的属性:
注意: onreadystatechange 事件被触发 4 次(0 - 4), 分别是: 0-1、1-2、2-3、3-4,对应着 readyState 的每个变化。
4、服务器页面 – ASP, PHP文件
5、AJAX的实现
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
function loadXMLDoc(){
// 1、创建对象
var request;
if(window.XMLHttpRequest){
request = new XMLHttpRequest();
}else{
request = new ActiveXObject('Microsoft.XMLHTTP');
}
// 2、初始化 设置类型和url. (请求报文,url是服务器的地址,后面也可以设置url参数。、/server是路径)
request.open('GET', 'http://127.0.0.1:8000/server?a=100&b=200');
// 设置请求头.一般会将用户的身份信息放入,用来在服务器当中验证
request.setRequestHeader('Content-Type','');
// 3、发送
// POST请求设置请求体在send里面设置
request.send();
// 4、事件绑定
// 由于AJAX的请求是异步的,所以需要通过回调函数获得响应 回调函数onreadystatechange
request.onreadystatechange = function () {
// 判断HTTP请求是否完成
if (request.readyState === 4) {
// 判断这个请求是否成功
if (request.status === 200) {
// 处理服务端返回的结果,这是response报文的信息
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
} else {
// HTTP请求还在继续...
}
}
alert('请求已发送,请等待响应...');
}
</script>
</head>
<body>
<div id="myDiv"><h2>使用 AJAX 修改该文本内容</h2></div>
// 通过点击事件触发AJAX
<button type="button" onclick="loadXMLDoc()">修改内容</button>
</body>
</html>
五、jQuery AJAX
jQuery的AJAX完全封装的是JavaScript的AJAX操作,从而简化相应的操作。使用的时候注意要引用jQuery库。
1、通用的发送AJAX的请求 – ajax(url, settings)
- jQuery在全局对象
jQuery
(也就是$
)绑定了ajax()
函数,可以处理AJAX请求。函数接受一个服务器的地址,以及一个可选的settings
对象。 - settings对象的选项:
// 发送一个GET请求,并返回一个JSON格式的数据
var jqxhr = $.ajax('/api/categories',{dataType:'json'});
用回调函数处理返回的数据和出错时的响应 – 类似Promise的链式操作
2、get()、post()
- 封装的是GET请求,当传入发送的数据data的时候,data将被转换成query附加到URL上。
- 封装的是POST请求,当传入发送的数据data的时候,根据contentType把data序列化成合适的格式。
(1)$.get(URL,callback); – 从服务器中请求数据
第二个参数是回调函数。第一个回调参数存有被请求页面的内容,第二个回调参数存有请求的状态。
// 使用 $.get() 方法从服务器上的一个文件中取回数据
$('button').click(function(){[
$.get('demo.php',function(data,status){
alert('数据' + data + '\n状态' + status);
})
]})
(2) $.post(URL,data,callback); – 向服务器提交数据。
$("button").click(function(){
$.post("/try/ajax/demo_test_post.php",
// c
{
name:"菜鸟教程",
url:"http://www.runoob.com"
},
function(data,status){
alert("数据: \n" + data + "\n状态: " + status);
});
});
3、getJSON
通过GET获取一个JSON对象
// 使用类似Promise对象的方法去处理回调
var jqxhr = $.getJSON('/path/to/resource', {
name: 'Bob Lee',
check: 1
}).done(function (data) {
// data已经被解析为JSON对象了
});
4、安全限制
-
问题:安全限制同用JavaScript写AJAX,无法进行跨域请求。
-
解决方案:如果需要使用JSONP,可以在
ajax()
中设置jsonp: 'callback'
,让jQuery实现JSONP跨域加载数据。 也可以使用CORS进行处理。
六、Axios
前端最热门的AJAX库。
1、引入Axios库
这里可以换成国内开源的,在BootCDN里面去找。
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
2、发送AJAX请求
-
jQuery使用回调函数处理返回的数据。而axios是使用promise进行处理。
-
即可以处理post请求,也可以处理get请求
// 绑定事件
btn.onclick = function(){
// 通用方法
axios({
//请求方法
method:'POST',
//url
url: '/axios-server',
//url参数
params:{
vip:10,
level:30
},
//头信息
headers:{
a:100,
b=200
},
//请求体参数
data:{
username:'admin',
password:'admin'
}
}).then(response=>{
// 获取响应信息
console.log(response);
// 获取响应状态码
console.log(response.status);
})
}
// 发送get请求
axios.get('/axios-server',{
//url参数
params:{
id:100,
vip:7
},
//请求头信息
headers:{
name:'yyx',
age:20
}
}).then(value =>{
console.log(value);
});
// then方法处理结果
七、fetch函数
fetch函数也可以发送AJAX请求
btn.onclick = function(){
fecth('http://127.0.0.1.8000/fetcg-server?vip=10',{
//请求方法
method:'POST',
// 请求头
header:{
name:'yyx'
},
// 请求体
body:'username=admin'
}).then(response=>{
return response.json();
}).then(response=>{
console.log(response);
});
}
八、AJAX的问题
1、问题 – 无法访问跨域资源
上面代码的URL使用的是相对路径。这意味着尝试加载的网页和 XML 文件都必须位于相同服务器上。如果使用绝对路径,默认情况下,JavaScript在发送AJAX请求时,URL的域名必须和当前页面完全一致。(浏览器的同源策略)
2、设置代理服务器实现
在同源域名下设一个代理服务器来转发,JavaScript负责把请求发送到代理服务器。代理服务器再把结果返回,这样就遵守了浏览器的同源策略。这种方式麻烦之处在于需要服务器端额外做开发。
'/proxy?url=http://www.sina.com.cn'
3、解决方案JSONP – 跨域加载数据
由于浏览器同源策略的限制,网页中无法通过AJAX请求非同源的接口数据。但是script标签不受浏览器同源策略的影响,可以通过src属性,请求非同源的js脚本。
JSONP的实现原理是通过script标签的src属性,请求跨域的数据接口,通过函数调用的形式,接受跨域接口响应回来的数据。
// 服务器接口的函数调用 - 调用refreshPrice
refreshPrice({"0000001":{"code": "0000001", ... });
// 回调函数
function refreshPrice(data) {
var p = document.getElementById('test-jsonp');
p.innerHTML = '当前价格:' +
data['0000001'].name +': ' +
data['0000001'].price + ';' +
data['1399001'].name + ': ' +
data['1399001'].price;
}
// src属性请求跨域的服务器接口,这个接口返回一个函数的调用。调用的函数通过查询字符串的形式告诉服务器要调用哪个文件(callback)
<script src='http://api.money.126.net/data/feed/0000001,1399001?callback=refreshPrice'></script>
4、新的跨域策略:CORS(Cross-Origin Resource Sharing)
跨域能否成功,取决于对方服务器是否愿意给你设置一个正确的Access-Control-Allow-Origin
,决定权始终在对方手中.
假设本域Origin是my.com
,外域是sina.com
,只要响应头Access-Control-Allow-Origin
为http://my.com
,或者是*
,本次请求就可以成功。
OPTIONS
请求
对于PUT、DELETE以及其他类型如application/json
的POST请求,在发送AJAX请求之前,浏览器会先发送一个OPTIONS
请求(称为preflighted请求)到这个URL上,询问目标服务器是否接受:
// 发送**`OPTIONS`请求**,询问服务器是否接受
OPTIONS /path/to/resource HTTP/1.1
Host: bar.com
Origin: http://my.com
Access-Control-Request-Method: POST
// 服务器必须响应并明确指出允许的Method
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://my.com
Access-Control-Allow-Methods: POST, GET, PUT, OPTIONS
Access-Control-Max-Age: 86400
// 浏览器确认服务器响应的Access-Control-Allow-Methods头确实包含将要发送的AJAX请求的Method,才会继续发送AJAX,否则,抛出一个错误。
OPTIONS
请求*
对于PUT、DELETE以及其他类型如application/json
的POST请求,在发送AJAX请求之前,浏览器会先发送一个OPTIONS
请求(称为preflighted请求)到这个URL上,询问目标服务器是否接受:
// 发送**`OPTIONS`请求**,询问服务器是否接受
OPTIONS /path/to/resource HTTP/1.1
Host: bar.com
Origin: http://my.com
Access-Control-Request-Method: POST
// 服务器必须响应并明确指出允许的Method
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://my.com
Access-Control-Allow-Methods: POST, GET, PUT, OPTIONS
Access-Control-Max-Age: 86400
// 浏览器确认服务器响应的Access-Control-Allow-Methods头确实包含将要发送的AJAX请求的Method,才会继续发送AJAX,否则,抛出一个错误。