本文主要介绍使用原生 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
2var data = ajax.responseText;
data = JSON.parse(data);
4) 事件监听
事件 | 含义 | 备注 |
---|---|---|
onreadystatechange | readyState属性的值发生改变,就会触发readyStateChange事件 | 常用 |
onloadstart | 请求发出 | |
onprogress | 正在发送和加载数据 | |
onabort | 请求被中止,比如用户调用了abort()方法 | |
onerror | 请求失败 | |
onload | 请求成功完成 | |
ontimeout | 用户指定的时限到期,请求还未完成 | |
onloadend | 请求完成,不管成功或失败 | 无备注 |
5) AJAX 发起请求代码含义
1 | // XMLHttpRequest 对象用来在浏览器与服务器之间传送数据。 |
二、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 | let data = request.responseText; |
####2 2、不带 <?xml ?>
返回数据的处理方式
这种方式,可以直接通过 request.responseXML
获得,请求得到的数据为 Object
对象。获取到其中的数据可使用操作 DOM 节点的方式获得。举例:1
2let 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 server
和 PORT=8002 node server
,在 server.js 中修改代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15else 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 都可以访问。
本文参考主要来自: