Ajax

Ajax | MDN

AJAX是异步的JavaScript和XML(Asynchronous JavaScript And XML)。简单点说,就是使用 XMLHttpRequest 对象与服务器通信。 它可以使用JSON,XML,HTML和text文本等格式发送和接收数据。AJAX最吸引人的就是它的“异步”特性,也就是说他可以在不重新刷新页面的情况下与服务器通信,交换数据,或更新页面。

创建一个简单的Ajax

  • 创建 XMLHttpRequest 对象

    1
    2
    3
    4
    5
    if (window.XMLHttpRequest) { // Mozilla, Safari, IE7+ ...
    httpRequest = new XMLHttpRequest();
    } else if (window.ActiveXObject) { // IE 6 and older
    httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
    }
  • 绑定onreadystatechange 事件

    1
    2
    3
    httpRequest.onreadystatechange = function(){
    // Process the server response here.
    };
  • 向服务器发送请求

    1
    2
    httpRequest.open('GET', 'http://www.example.org/some.file', true);
    httpRequest.send();

完整的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function ajax(url, cb) {
let xhr;
if(window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
xhr = ActiveXObject("Microsoft.XMLHTTP");
}
xhr.onreadystatechange = function() {
if(xhr.readyState == 4 && xhr.status == 200) {
cb(xhr.responseText);
}
}
xhr.open('GET', url, true);
xhr.send();
}

httpRequest.readyState的值

  • 0 (未初始化) or (请求还未初始化)
  • 1 (正在加载) or (已建立服务器链接)
  • 2 (加载成功) or (请求已接受)
  • 3 (交互) or (正在处理请求)
  • 4 (完成) or (请求已完成并且响应已准备好)

访问服务端返回的数据

  • httpRequest.responseText
    • 服务器以文本字符的形式返回
  • httpRequest.responseXML
    • 以 XMLDocument 对象方式返回,之后就可以使用JavaScript来处理

GET 注意事项

  • 如果不设置响应头 Cache-Control: no-cache 浏览器将会把响应缓存下来而且再也不无法重新提交请求。你也可以添加一个总是不同的 GET 参数,比如时间戳或者随机数 (详情见 bypassing the cache)

POST 请求

POST请求则需要设置RequestHeader告诉后台传递内容的编码方式以及在send方法里传入对应的值

1
2
3
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type": "application/x-www-form-urlencoded");
xhr.send("key1=value1&key2=value2");

Ajax与cookie

  • ajax会自动带上同源的cookie,不会带上不同源的cookie
  • 可以通过前端设置withCredentials为true, 后端设置Header的方式让ajax自动带上不同源的cookie,但是这个属性对同源请求没有任何影响。会被自动忽略。

withCredentials | MDN

1
2
3
4
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://example.com/', true);
xhr.withCredentials = true;
xhr.send(null);

什么是Ajax?

AJAX是异步的JavaScript和XML(Asynchronous JavaScript And XML)。

简单点说,就是使用 XMLHttpRequest 对象与服务器通信。 它可以使用JSON,XML,HTML和text文本等格式发送和接收数据。

AJAX最吸引人的就是它的“异步”特性,也就是说它可以在不重新刷新页面的情况下与服务器通信,交换数据,或更新页面。

你可以使用AJAX最主要的两个特性做下列事:

  • 在不重新加载页面的情况下发送请求给服务器。
  • 接受并使用从服务器发来的数据。

Step 1 – 怎样发送http请求

为了使用JavaScript向服务器发送一个http请求,你需要一个包含必要函数功能的对象实例。这就是为什么会有 XMLHttpRequest 的原因。

1
2
3
4
5
6
// Old compatibility code, no longer needed.
if (window.XMLHttpRequest) { // Mozilla, Safari, IE7+ ...
httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE 6 and older
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}

发送一个请求后,你会收到响应。在这一阶段,你要告诉XMLHttp请求对象是由哪一个JavaScript函数处理响应,在设置了对象的 onreadystatechange 属性后给他命名,当请求状态改变时调用函数。

1
httpRequest.onreadystatechange = nameOfTheFunction;

要注意的是,函数名后没有参数,因为你把一个引用赋值给了函数,而不是真正的调用了它。

如果不使用函数名的方式,你还可以用JavaScript的匿名函数响应处理的动作,就像下面这样:

1
2
3
httpRequest.onreadystatechange = function(){
// Process the server response here.
};

接下来,声明当你接到响应后要做什么,你要发送一个实际的请求,通过调用HTTP请求对象的 open()send() 方法,像下面这样:

1
2
httpRequest.open('GET', 'http://www.example.org/some.file', true);
httpRequest.send();
  • 第三个参数是可选的,用于设置请求是否是异步的。如果设为 true (默认值),即开启异步,JavaScript就不会在此语句阻塞,使得用户能在服务器还没有响应的情况下与页面进行交互。

send() 方法的参数可以是任何你想发送给服务器的内容,如果是 POST 请求的话。发送表单数据时应该用服务器可以解析的格式,像查询语句:

1
"name=value&anothername="+encodeURIComponent(myVar)+"&so=on"

如果你使用 POST 数据,那就需要设置请求的MIME类型。比如,在调用 send() 方法获取表单数据前要有下面这个:

1
httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

Step 2 – 处理服务器响应

首先,函数要检查请求的状态。如果状态的值是 XMLHttpRequest.DONE (对应的值是4),意味着服务器响应收到了并且是没问题的,然后就可以继续执行。

1
2
3
4
5
if (httpRequest.readyState === XMLHttpRequest.DONE) {
// Everything is good, the response was received.
} else {
// Not ready yet.
}

全部readyState状态值:

  • 0 (未初始化) or (请求还未初始化)
  • 1 (正在加载) or (已建立服务器链接)
  • 2 (加载成功) or (请求已接受)
  • 3 (交互) or (正在处理请求)
  • 4 (完成) or (请求已完成并且响应已准备好)

接下来,点击HTTP响应的 response code。 可能的响应码都已经列在 W3C这个列表里。

通过检查响应码 200 OK 判断AJAX有没有成功。

1
2
3
4
5
6
7
if (httpRequest.status === 200) {
// Perfect!
} else {
// There was a problem with the request.
// For example, the response may have a 404 (Not Found)
// or 500 (Internal Server Error) response code.
}

在检查完请求状态和HTTP响应码后, 你就可以用服务器返回的数据做任何你想做的了。你有两个方法去访问这些数据:

  • httpRequest.responseText – 服务器以文本字符的形式返回
  • httpRequest.responseXML – 以 XMLDocument 对象方式返回,之后就可以使用JavaScript来处理

注意上面这一步只在你发起异步请求时有效(即 open() 的第三个参数未特别指定或设为 true)。

Step 3 – 一个简单的例子

这个JavaScript会请求一个HTML文档 test.html,包含 “I’m a test” 内容。然后我们 alert() 响应的内容。注意这个例子我们只是用了JavaScript,没有用jQuery。而且,HTML,XML和PHP文件都要放在用一个目录下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<button id="ajaxButton" type="button">Make a request</button>

<script>
(function() {
var httpRequest;
document.getElementById("ajaxButton").addEventListener('click', makeRequest);

function makeRequest() {
httpRequest = new XMLHttpRequest();

if (!httpRequest) {
alert('Giving up :( Cannot create an XMLHTTP instance');
return false;
}
httpRequest.onreadystatechange = alertContents;
httpRequest.open('GET', 'test.html');
httpRequest.send();
}

function alertContents() {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
alert(httpRequest.responseText);
} else {
alert('There was a problem with the request.');
}
}
}
})();
</script>

在这个例子中:

  • 用户点击 “Make a request” 按钮;
  • 事件处理调用 makeRequest() 函数;
  • 请求已通过然后(onreadystatechange)传给 alertContents() 执行。
  • alertContents() 检查返回的响应是否OK,然后 alert() test.html 文件内容。

在通信错误的事件中(例如服务器宕机),在访问响应状态 onreadystatechange 方法中会抛出一个例外。为了缓和这种情况,则可以使用 try...catchif...then 语句包裹起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function alertContents() {
try {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
alert(httpRequest.responseText);
} else {
alert('There was a problem with the request.');
}
}
}
catch( e ) {
alert('Caught Exception: ' + e.description);
}
}

Step 4 – 处理 XML 响应

首先,我们创建一个稍后将要请求的有效的 XML 文档。文档(test.html)包含以下内容:

1
2
3
4
<?xml version="1.0" ?>
<root>
I'm a test.
</root>

在脚本里我们只需要把请求行改为:

1
2
3
...
onclick="makeRequest('test.xml')">
...

然后在 alertContents() 里,我们把 alert(httpRequest.responseText) 改为:

1
2
3
var xmldoc = httpRequest.responseXML;
var root_node = xmldoc.getElementsByTagName('root').item(0);
alert(root_node.firstChild.data);

Step 5 – 处理数据

首先要添加一个文本到 HTML 中以方便用户输入名字:

1
2
3
4
5
6
<label>Your name:
<input type="text" id="ajaxTextbox" />
</label>
<span id="ajaxButton" style="cursor: pointer; text-decoration: underline">
Make a request
</span>

还要添加事件处理程序,从表单中获取用户数据连同服务器端的UTL一并发送给 makeRequest() 函数:

1
2
3
4
document.getElementById("ajaxButton").onclick = function() {
var userName = document.getElementById("ajaxTextbox").value;
makeRequest('test.php',userName);
};

我们还要修改 makeRequest() 让它接受用户数据并将其发给服务器。把请求方法从 GET 改为 POST,把数据作为参数让 httpRequest.send() 调用。

1
2
3
4
5
6
7
8
9
function makeRequest(url, userName) {

...

httpRequest.onreadystatechange = alertContents;
httpRequest.open('POST', url);
httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
httpRequest.send('userName=' + encodeURIComponent(userName));
}

为了在 alertContents() 中使用这个数据,我们可不能只是alert responseText ,我们要解析它并 alert computedString,我们想要的属性:

1
2
3
4
5
6
7
8
9
10
function alertContents() {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
var response = JSON.parse(httpRequest.responseText);
alert(response.computedString);
} else {
alert('There was a problem with the request.');
}
}
}
1
2
3
4
5
// test.php
$name = (isset($_POST['userName'])) ? $_POST['userName'] : 'no name';
$computedString = "Hi, " . $name;
$array = ['userName' => $name, 'computedString' => $computedString];
echo json_encode($array);