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超文本传输协议,协议规定了浏览器和万维网服务器之间互相通信的规则。
1、请求报文 – 浏览器向服务器请求
格式和参数:
-
请求行 (请求类型-- POST GET) (url路径) (HTTP版本)
-
请求头 HOST:
Cookie:
Content-type:
User-Agent:
-
空行
-
请求体 uesrname=admin&&password=admin
每次请求的时候,是将以上四个部分进行拼接发送给服务端。
2、响应报文 – 服务器响应
格式和参数:
-
响应行 (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>
五 、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,否则,抛出一个错误。