AJAX

本文主要介绍使用原生 JS 实现 AJAX 请求。

浏览器发起请求的几种方式分别为:

  • <form> 表单,但是会刷新页面或者打开新页面
  • <a> 可以发起 get 请求,但是也会刷新页面或者打开新页面
  • <image> 可以发起 get 请求,但是只能以图片形式展示
  • <link> 可以发起 get 请求,但是只能以 CSS 、favicon 的形式展示
  • <script> 可以发起 get 请求,但是只能以脚本的形式运行
  • ajax 可以发起 get 、 post 、 put 、 delete 等请求,且不限形式

这里主要讲解一下 AJAX( Asynchronous JavaScript and XML => 异步的 JavaScript 和 XML)。满足以下条件即为使用 AJAX :

  • 使用 XMLHttpRequest 发起请求
  • 服务器返回 XML 格式的字符串
  • JS 解析 XML ,并更新局部页面

一、如何使用 AJAX

1、目录结构

这边文章中的示例代码目录结构为:

  • server.js
  • index.html
  • style.css
  • main.js

对应的文件中代码为:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//========server.js=========
var http = require("http");
var fs = require("fs");
var url = require("url");
var port = process.env.PORT || 8888;

var server = http.createServer((req,res)=>{
let temp = url.parse(req.url,true);
let path = temp.pathname;
let query = temp.query;
let method = req.method;

if(path === '/'){
res.statusCode = 200;
let str = fs.readFileSync('./index.html','utf8');
res.setHeader('Content-Type','text/html;charset=utf-8');
res.write(str);
res.end();
}else if(path === '/style.css'){
res.statusCode = 200;
let str = fs.readFileSync('./style.css','utf8');
res.setHeader('Content-Type','text/css');
res.write(str);
res.end();
}else if(path === '/main.js'){
res.statusCode = 200;
let str = fs.readFileSync('./main.js','utf8');
res.setHeader('Content-Type','application/javascript');
res.write(str);
res.end();
}else if(path === '/xxx'){
res.statusCode = 200;
res.setHeader('Content-Type','text/xml');
let str = `
<?xml version="1.0" encoding="UTF-8"?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
`;
res.write(str);
res.end();
}else{
res.statusCode = 400;
res.write('呜呜呜');
res.end();
}
})
server.listen(port);
console.log(
"监听 " +
port +
" 成功\n打开 http://localhost:" +
port
);
//========index.html=========
<!DOCTYPE>
<html>
<head>
<link rel='stylesheet' href='/style.css'>
</head>
<body>
<button id="btn">发起请求</button>
<script type='text/javascript' src='/main.js'></script>
<script>
btn.addEventListener('click',function(){
let http = new XMLHttpRequest();
http.open('get','/xxx');
http.send();
})
</script>
</body>
</html>
//========style.css=========
body{
background:#ededed;
}
h1{
color:#f00;
}
h2{
color:#666;
}
//========main.js=========
console.log(1);

以上,在当前目录下打开终端,并输入 PORT=8001 node server ,在浏览器中输入 localhost:8001

2、XMLHttpRequest 的常用属性和方法

1) readyState

readyState 是一个只读属性,用一个整数和对应的常量,表示 XMLHttpRequest 请求当前所处的状态。

常量 意义
0 UNSENT 表示XMLHttpRequest实例已经生成,但是open()方法还没有被调用
1 OPENED 表示send()方法还没有被调用,仍然可以使用setRequestHeader(),设定HTTP请求的头信息
2 HEADERS_RECEIVED 表示send()方法已经执行,并且头信息和状态码已经收到
3 LOADING 表示正在接收服务器传来的body部分的数据
4 DONE 表示服务器数据已经完全接收,或者本次接收已经失败了。
2) status

status 属性为只读属性,表示本次请求所得到的HTTP状态码,它是一个整数。一般来说,如果通信成功的话,这个状态码是200。

含义/英 含义/中
200 OK 访问正常
301 Moved Permanently 永久移动
302 Move temporarily 暂时移动
304 Not Modified 未修改
307 Temporary Redirect 暂时重定向
401 Unauthorized 未授权
403 Forbidden 禁止访问
404 Not Found 未发现指定网址
500 Internal Server Error 服务器发生错误
3) 返回数据

responseText 属性返回从服务器接收到的字符串,该属性为只读。如果本次请求没有成功或者数据不完整,该属性就会等于 null 。如果服务器返回的数据格式是 JSON ,就可以使用 responseText 属性。

1
2
var data = ajax.responseText;
data = JSON.parse(data);

4) 事件监听
事件 含义 备注
onreadystatechange readyState属性的值发生改变,就会触发readyStateChange事件 常用
onloadstart 请求发出
onprogress 正在发送和加载数据
onabort 请求被中止,比如用户调用了abort()方法
onerror 请求失败
onload 请求成功完成
ontimeout 用户指定的时限到期,请求还未完成
onloadend 请求完成,不管成功或失败 无备注
5) AJAX 发起请求代码含义
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
31
// XMLHttpRequest 对象用来在浏览器与服务器之间传送数据。
var request = new XMLHttpRequest();

// 指定通信过程中状态改变时的回调函数
request.onreadystatechange = function(){
// 通信成功时,状态值为4
if (request.readyState === 4){
if (request.status === 200){
console.log(request.responseText);
} else {
console.error(request.statusText);
}
}
};

request.onerror = function (e) {
console.error(xhr.statusText);
};

// open方式用于指定HTTP动词、请求的网址、是否异步
//void open(
// string method, 表示HTTP动词,比如“GET”、“POST”、“PUT”和“DELETE”。
// string url, 表示请求发送的网址
// optional boolean async, 格式为布尔值,默认为true,表示请求是否为异步。如果设为false,则send()方法只有等到收到服务器返回的结果,才会有返回值。
// optional string user, 表示用于认证的用户名,默认为空字符串
// optional string password 表示用于认证的密码,默认为空字符串
//);
request.open('GET', '/xxx', true);

// 发送HTTP请求
request.send(null);

二、XML

根据《一、AJAX》中的代码,服务服务器返回的是一段 xml 代码,那传到浏览器该如何处理呢?
正常的 XML 数据格式为:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>

两种处理方式:

1、带 <?xml ?> 返回数据的处理方式

1
2
3
4
let data = request.responseText;
let parser = new DOMParser();
let xmlDoc = parser.parseFromString(data,'text/xml')
let toName = xmlDoc.getElementsByTagName('to')[0].textContent;

####2 2、不带 <?xml ?> 返回数据的处理方式
这种方式,可以直接通过 request.responseXML 获得,请求得到的数据为 Object 对象。获取到其中的数据可使用操作 DOM 节点的方式获得。举例:

1
2
let data = request.responseXML;
let toName = data.getElementsByTagName('to')[0].textContent; // Tove

以上两种方法,获取到的 xmlDoc 和 data 都是 DOM 节点,可以通过操作 DOM 节点的方式获得里面的数据。

三、JSON

以上返回的 xml 格式数虽然能够满足应用层面上的需求,但是对于返回来的数据我们还需使用 DOM API 完成对返回数据的处理。在这种情况下,JSON 就应运而生。JSON( JavaScript Object Notation ) 是一门新的轻量级的数据交换语言,这门语言不同于 JavaScript 但是又借鉴了 JavaScript 的很多东西。
比较下 JSON 和 JavaScript 二者的不同
1、 JSON 中无 undefined 和 Function
2、 JSON 中的字符串首尾必须是 “”

JavaScript JSON
Object {name:’summer’} {“name”:”sumer”}
- undefined 没有
- null null
Array [‘a’,’b’]] [“a”,”b”]
function function fn(){} 没有
String ‘summer’ “summer”
- var a = {};a.self = a; 无变量,无法实现
- {proto:} 没有原型链

根据以上对 JSON 的了解,我们尝试着在 server.js 中的 /xxx 的请求返回数据改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
let str = `
{
"to":"Tove",
"from":"Jani",
"heading":"Reminder",
"body":"Don't forget me this weekend!"
}
`;
res.write(str);
...
//=========index.html====返回数据修改=====
let data = JSON.parse(request.responseText); //返回的数据一定是字符串,通过 JSON.parse() 转换成对象
...

四、同源策略

为什么表单提交没有跨域问题而 ajax 有跨域问题?
主要原因是原页面使用 form 提交到另一个域名之后,原页面的脚本无法获取新页面的中的内容,所以浏览器认为这是安全的。而 AJAX 是可以读取响应内容的,因此浏览器不允许这样。如果我们查看控制台的 Network 会发现请求已经发送出去,只是拿不到响应而已。所以浏览器策略的本质是:一个域名的 JS ,在未经允许的情况下,不得读取另一个域名中的内容,但浏览器并不阻止你向另一个域名发送请求。这个问题我们可以自己尝试这写个 Demo 试一下,再次不进行过多介绍。

只有域名一模一样才能够发起 AJAX 请求。关于域名一模一样可以查看我的另一篇文章:浏览器同源策略。只有协议、域名、端口号一致才能发起 AJAX 请求。

五、CORS 跨域

如果不同源并且又需要完成跨域请求,该如何处理呢?
方法一是使用 JSONP 的方法实现,具体请查看《JSONP简介》

另一个方法是使用 CORS(Cross-Origin Resource Sharing, 跨源资源共享):
我们根据 一、如何使用 AJAX -> 目录结构 中的代码分别分两个端口启动服务,打开两个终端分别输入 PORT=8001 node serverPORT=8002 node server ,在 server.js 中修改代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
else if(path === '/xxx'){
res.statusCode = 200;
res.setHeader('Content-Type','text/xml');
res.setHeader('Access-Control-Allow-Origin','http://example.com:8002')
let str = `
{
"to":"Tove",
"from":"Jani",
"heading":"Reminder",
"body":"Don't forget me this weekend!"
}
`;
res.write(str);
res.end();
}

主要是添加 res.setHeader('Access-Control-Allow-Origin',url) , url 是向你发起请求的 url 地址。或者可以将 url 设置为 ‘*’ ,则表示此接口是公开的,所有的 url 都可以访问。


本文参考主要来自: