浏览器与服务器之间,采用HTTP协议通信。用户在浏览器地址栏键入一个网址,或者通过网页表单向服务器提交内容,这时浏览器就会向服务器发出HTTP请求。
1999年,微软公司发布IE浏览器5.0版,第一次引入新功能:允许JavaScript脚本向服务器发起HTTP请求。这个功能当时并没有引起注意,直到2004年Gmail发布和2005年Google Map发布,才引起广泛重视。2005年2月,AJAX这个词第一次正式提出,指围绕这个功能进行开发的一整套做法。从此,AJAX成为脚本发起HTTP通信的代名词,W3C也在2006年发布了它的国际标准。
具体来说,AJAX包括以下几个步骤。
- 创建AJAX对象
- 发出HTTP请求
- 接收服务器传回的数据
- 更新网页数据
概括起来,就是一句话,AJAX通过原生的XMLHttpRequest对象发出HTTP请求,得到服务器返回的数据后,再进行处理。
AJAX可以是同步请求,也可以是异步请求。但是,大多数情况下,特指异步请求。因为同步的Ajax请求,对浏览器有“堵塞效应”。
注意,AJAX只能向同源网址(协议、域名、端口都相同)发出HTTP请求,如果发出跨源请求,就会报错。
1、AJAX 是什么?有什么作用?
AJAX:是对Asynchronous JavaScript and XML的简写,是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。这一技术能够向服务器请求额外的数据而无需从新加载页面。
作用:传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页面。而通过使用ajax可以在后台与服务器进行少量数据交换, 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
2、Ajax和XMLHttpRequest
Ajax
核心的技术是XMLHttpRequest
对象(简称XHR)。我们通常将Ajax
等同于XMLHttpRequest
,但细究起来它们两个是属于不同维度的2个概念。
Ajax:(摘自what is Ajax)AJAX stands for Asynchronous JavaScript and XML. AJAX is a new technique for creating better, faster, and more interactive web applications with the help of XML, HTML, CSS, and Java Script.
AJAX is based on the following open standards:
- Browser-based presentation using HTML and Cascading Style Sheets (CSS).
- Data is stored in XML format and fetched from the server.
- Behind-the-scenes data fetches using XMLHttpRequest objects in the browser.
- JavaScript to make everything happen.
从上面的解释中可以知道:Ajax
是一种技术方案,但并不是一种新技术。它依赖的是现有的CSS/HTML/Javascript
,而其中最核心的依赖是浏览器提供的 XMLHttpRequest
对象,是这个对象使得浏览器可以发出HTTP
请求与接收HTTP
响应。
所以用一句话来总结两者的关系,就是:我们使用XMLHttpRequest
对象来发送一个Ajax
请求。
3、XMLHttpRequest对象
1、什么是XMLHttpRequest?
-
XMLHttpRequest
是原生JS的一个内置对象,用来在浏览器与服务器之间传送数据,一旦拿到服务器返回的数据,AJAX不会刷新整个网页,而是只更新相关部分,从而不打断用户正在做的事情。XMLHttpRequest
是AJAX
技术的核心,学习AJAX
实质上就是在学习XMLHttpRequest
。
2、如何创建XMLHttpRequest对象:
- 一般使用new关键字进行创建,然后赋值给一个变量,如下:
var xhr = new XMLHttpRequest();
4、XMLHttpRequest对象的常用属性
1、readyState
只读属性,表示XMLHttpRequest
请求当前所处的状态,共有五个数字值(0,1,2,3,4,5)。
- 0:表示
XMLHttpRequest
实例已经生成,但是open()
方法还没有被调用。 - 1:表示已调用
open
方法,但还未调用send
方法(请求还未被发送出去),仍然可以使用setRequestHeader()
,设定HTTP
请求的头信息。 - 2:表示
send
方法已调用,数据已发送,并且服务器接收到了请求。 - 3:表示服务器正在传输数据。
- 4:表示数据传输完成。
在通信过程中,每当发生状态变化的时候,readyState属性的值就会发生改变。这个值每一次变化,都会触发readyStateChange事件。
2、status
只读属性,表示本次请求所得到的HTTP状态码,返回一个整数。一般来说,如果通信成功的话,这个状态码是200。常用的有如下几个状态码:
- 200:OK(正常访问);
- 301:Moved Permanently(永久移动);
- 302:Moved temporarily(暂时移动);
- 304:Not Modified(未修改);
- 307:Temporary Redirect(暂时重定向);
- 401:Unauthorized (未授权);
- 403:Forbidden(禁止访问);
- 404:Not Found(未找到该网址);
- 500:Internal Server Error (找到网址但服务器发生错误);
基本上,只有200和304的状态码,表示服务器返回是正常状态。|
3、 statusText
与status属性类似,返回本次请求的状态,不同点在于,status只返回一个数字,而该属性返回一个字符串 ,包含整个状态信息,比如”200 OK“|
4、responseType
responseType属性用来指定服务器返回数据(xhr.response
)的类型。可通过对该属性赋值来指定接收的数据类型,默认为字符串,有如下几种数据类型:
-
text
:以字符串形式接收数据; -
json
:以json对象形式接收数据; -
blob
:blob对象; -
ArrayBuffer
:ArrayBuffer对象;
5、response、responseText、responseXML
三者都是服务器返回的数据,如果数据不完整或者获取失败,它们的值就为null。
不同点:
response返回的是数据的主体部分,可以为任何类型(数组,json,XML,字符串等);
responseText返回从服务器接收到的字符串。该属性为只读。如果本次请求没有成功或者数据不完整,该属性就会等于null。如果服务器返回的数据格式是JSON,就可以使用responseText属性;
//返回JSON格式的字符串
var data = ajax.responseText;
//把JSON格式的字符串转换为JavaScript对象
data = JSON.parse(data);
- responseXML返回从服务器接收到的Document对象,该属性为只读。如果本次请求没有成功,或者数据不完整,或者不能被解析为XML或HTML,该属性等于null。该值返回的数据会被直接解析DOM;
5、XMLHttpRequest对象的常用方法
1、abort()
abort方法用来终止已经发出的HTTP请求。
2、getAllResponseHeaders()
getAllResponseHeaders
方法返回服务器发来的所有HTTP头信息。格式为字符串,每个头信息之间使用CRLF分隔,如果没有受到服务器回应,该属性返回null,该方法不需要接受参数。
3、getResponseHeader()
getResponseHeader
方法返回HTTP头信息指定字段的值,如果还没有收到服务器回应或者指定字段不存在,则该属性为null。该方法需要接受一个参数,用来返回指定字段的值。
4、open()
XMLHttpRequest
对象的open方法用于指定发送HTTP请求的参数,常用的有三个参数:
第一个参数:请求的类型(常用get或者post);
第二个参数是接口名和:这里要分两种情况:
get请求时:接口名+请求参数(键值对形式);post请求时:只需要接口名(需要传递的参数写在
send
方法里);第三个参数:一个布尔值,指定是否异步(true为异步,false为同步,通常为true,默认为true);
第四和第五个参数:填写用于认证的用户名和密码;
5、send()
send方法用于实际发出HTTP请求。如果不带参数,就表示HTTP请求只包含头信息,也就是只有一个URL,典型例子就是GET请求;如果带有参数,就表示除了头信息,还带有包含具体数据的信息体,典型例子就是POST请求。
如果是POST请求还要在open()
之后、send()
之前使setRequestHeader
方法设置HTTP头信息。
ajax.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
6、setRequestHeader()
setRequestHeader
方法用于设置HTTP头信息。该方法必须在open()之后、send()之前调用。
6、XMLHttpRequest对象的事件以及对应的事件监听接口
7、 前后端开发联调需要注意哪些事情?后端接口完成前如何 mock 数据?
mock数据指的是在后端开发没有完成时,前端可以通过mock方法搭建本地服务器,模拟后台数据来实现数据交互的效果
前后端开发联调需要注意哪些事情:
约定数据:有哪些需要传输的数据,数据类型是什么。
约定接口:确定接口名称以及请求和响应的方法(get or post),请求的参数名称,响应的数据格式。
根据这些约定整理成接口文档。
后端接口完成前如何 mock 数据:
根据接口文档,使用假数据来验证制作的网页响应和接口是否正常。
可以使用server-mock。
3,可以搭建php本地服务器用,php写脚本提供临时数据。
8、点击按钮,使用 ajax 获取数据,如何在数据到来之前防止重复点击?
利用布尔值设置一个状态锁,在触发ajax前和数据到来的时候布尔值设置为true,是不锁定的;发送数据之后布尔值为false,是锁定的。若重复点击在数据没有到来之前也就是布尔值为true时,会把重复点击忽略。
//利用布尔值作为状态锁
var lock = true;
btn.addEventListener('click',function(){
//用户重复点击,数据没有到来之前直接return,忽略重复点击
if(!lock ){
return;
}
ajax({
...
//数据到来,布尔值设为true
lock = true;
})
xhr.open(...,...,...);
xhr.send();
//发送ajax请求,这时数据还没有到来,布尔值设为false
lock = false;
});
9、封装AJAX实现加载更多
这里使用server-mock来mock数据。server-mock是一款nodejs命令行工具,用于搭建web服务器,模拟网站后端,方便前端开发者Mock数据。
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<style>
ul,li{
margin; 0;
padding: 0
}
#ct li{
list-style: none;
border:1px solid #ccc;
padding:10px;
margin-top: 10px;
cursor:pointer;
}
#load-more{
display: block;
margin:10px auto;
text-align: center;
cursor: pointer;
}
.btn{
display: inline-block;;
height: 40px;
line-height: 40px;
width: 80px;
border: 1px solid #E27272;
border-radius: 3px;
text-align: center;
text-decoration: none;
color:#E27272;
}
.btn:hover{
background: green;
color:#fff;
}
</style>
</head>
<body>
<ul id="ct">
</ul>
<a id="load-more" class="btn" href="#">
加载更多
</a>
<script>
var btn = document.querySelector('#load-more');
var ct = document.querySelector('#ct');
var pageIndex = 0;
//设置状态锁,防止数据到来之前用户重复点击
var isDataArrive = true;
btn.addEventListener('click',function (e) {
e.preventDefault();
if (!isDataArrive) {
return;
}
loadData(function(news){
renderPage(news);
})
})
function loadData(callback){
ajax({
type: 'get',
url: '/loadMore',
data: {
index: pageIndex,
length: 5
},
onSuccess: callback,
onError: function(){
console.log('出错了')
}
})
}
function renderPage(results){
var fragment = document.createDocumentFragment();
for (var i = 0; i < results.length; i++) {
var node = document.createElement('li');
node.innerText = results[i];
fragment.appendChild(node);
}
ct.appendChild(fragment)
}
function ajax(options){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if (xhr.readyState === 4) {
if (xhr.status === 200 || xhr.status === 304) {
//与后端约定好,传输的数据类型为JSON字符串,JSON.parse()用来把JSON字符串解析为原生JavaScript值
var results = JSON.parse(xhr.responseText);
options.onSuccess(results);
pageIndex = pageIndex + 5;
}else{
options.onError();
}
//数据到来,布尔值设为true
isDataArrive = true
}
}
var str = '';
for(var key in options.data){
str += key + '=' + options.data[key] + '&';
}
str = str.substr(0, str.length-1);
if(options.type.toLowerCase() === 'get'){
xhr.open(options.type, options.url + '?' + str, true)
xhr.send()
}
if(options.type.toLowerCase() === 'post'){
xhr.open('post', options.url, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(str);
}
//发送ajax请求,这时数据还没有到来,布尔值设为false
isDataArrive = false;
}
</script>
</body>
</html>
router.js
app.get('/loadMore',function(req,res){
var curIdx = req.query.index;
var len= req.query.length;
var data = [];
for (var i = 0; i < len; i++) {
data.push('新闻' + (parseInt(curIdx) + i))
}
res.send(data);
})
app.post('/loadMore',function(req,res){
var curIdx = req.body.index;
var len= req.body.length;
var data = [];
for (var i = 0; i < len; i++) {
data.push('新闻' + (parseInt(curIdx) + i))
}
res.send(data);
})
每次点击加载更多按钮都会发送一条AJAX请求,数据没回来之前,重复点击会被忽略,数据到来后会渲染到页面上出现5条新闻。