Ajax简述

在以前的前端页面中,如果想要更新网页,则需要重新加载所有数据。如今有Ajax,我们可以接受服务器的数据,进行局部更新网页,很多新闻类App的加载更多就是通过Ajax实现的。

  • 目的:实现网页的增量更新
  • 优点:节约资源、提高用户体验
  • 实现:通过XMLHttpRequest对象实现Ajax请求
  • 安全: 不允许跨域访问
  • 进程: 异步执行

相关教程:

https://www.bilibili.com/video/BV1ji4y1876Y

https://www.w3school.com.cn/js/js_ajax_intro.asp

要实现一个Ajax请求通常需要一下几步:

  1. node环境搭建
  2. 创建工程文件
  3. js 代码编写

先说搭建环境,我们可以从nodejs的官网下载TLS版的node安装包,安装完成并且确保在环境变量下。然后再使用npm install express -g --save安装express框架。如果你想要后端服务实时刷新,可以安装nodemon库。

接着我们创建工程目录,大致形式如下:

1
2
3
4
5
6
7
8
9
.
node_modules
public
fontend.html # 编写前端html+js代码,注意,这里我只给出了js代码,html代码没有内容
backend.js # 编写后端node代码
yarn.lock # 该文件可能会有其他的名字
package.json
package-lock.json

最后代码如下,首先是前端代码fontend.html,其次是后端代码backend.js。

fontend.html

1
2
3
4
5
6
7
8
9
10
11
// 1. 创建ajax对象
var xhr = new XMLHttpRequest();
// 2. 告诉Ajax请求地址以及请求方式
xhr.open('GET','http://localhost:3000/get');
// 3. 发送请求
xhr.send();
// 4. 获取服务端与客户端的响应数据
xhr.onload = function() {
// 获取响应数据
console.log(xhr.responseText);
}

backend.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 引入express框架
const express = require('express');
// 引入路径处理模块
const path = require('path');
// 创建web服务器
const app = express();

//静态资源访问服务功能
app.use(express.static(path.join(__dirname,'public')));
app.get('/first',(req,res) => {
res.send("Hello xajA!");
});
// 监听端口
app.listen(3000,'localhost');

// 控制台提示输出
console.log("服务器启动成功!");

最后我们执行node backend.js(如果你安装了nodemon,可以执行nodemon backend.js),访问网页:http://localhost:3000/fontend.html,打开控制台,就可以看到Hello xajA!的输出了。

Ajax实践

在最佳实践里,我会通过一个例子来阐述Ajax请求时需要哪些步骤,同时前端使用Javascript原生代码实现,后端使用nodejs的express框架实现,都是一些很简单的代码,请耐心看下去。

具体例子如下。通常我们的请求会有GET请求和POST请求,如果一个网页需要很多Ajax请求,我们通常希望把这些请求的代码封装成一个函数,这样可以避免代码冗余,增加代码复用。好了话不多说,直接上代码。

GET请求,请求内容放在URL中
POST请求,请求内容放在请求体中

font-end 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
// 需要注意的是,数据传输时只能传输字符串,所以我们在调用ajax的send和xhr.responseText时,需要转化为字符串或JSON对象

function ajax(options){
var xhr = new XMLHttpRequest();
// get 请求
if(options.type == 'GET'){
// get方法构造的链接形式: http://www.example.com/get?name=neo&age=180
// 路径和参数之间用 ? 问号分隔
// 参数和参数之间用 & 地址符分隔
// 参数的value写在key后面,用 = 等号链接
xhr.open("GET",options.url + options.params);
xhr.send();
}
// post 请求
else if(options.type == "POST"){
xhr.open("POST",options.url);
// post方法必须有请求参数格式,即Content-Type
// 如果是JSON传递数据,那么Content-Type类型要改为JSON
// 如果不是JSON传递数据,那么Content-Type类型通常是x-www-form-urlencoded
xhr.setRequestHeader("Content-Type","application/json");
// 将JSON对象转换为字符类型
var strParams = JSON.stringify(options.params);
xhr.send(strParams);
}

xhr.onload = function(){
// 将返回的字符串解析为JSON,后端服务器返回值必须为json
var jsonRes = JSON.parse(xhr.responseText)
// 封装的函数里调用的options里自定义的方法,用户可以灵活的操作返回值
options.success(jsonRes);
}
}

// 构造POST请求
var postObj = {
type: "POST",
url: "http://localhost:3000/post",
params: {
username: "lisi",
age: 109
},
// data 这里的形参是为了给封装的ajax函数调用传递实参
success: function(data){
console.log(data);
}
}

// 构造GET请求
var getObj = {
type: "GET",
url: "http://localhost:3000/get?",
params: "username=zhangsan&age=19",
success: function(data){
console.log(data);
}
}

// 调用封装的ajax请求函数
ajax(getObj);

back-end.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
// 引入express框架,记得安装express
const express = require('express');
// 引入路径处理模块
const path = require('path');
// 创建web服务器
const app = express();
// 引入处理post请求体的模块,使用yarn add body-parser 或者 npm install body-parser 安装
const bodyParser = require('body-parser');
// 给web服务器添加body-parser模块的特性
app.use(bodyParser.urlencoded());
app.use(bodyParser.json());

//静态资源访问服务功能
app.use(express.static(path.join(__dirname,'public')));

// 处理GET请求,返回值为GET请求的内容
// 如果GET请求链接为: http://www.example.com/get?username=zen&age=19
// 那么返回的内容则为:username:zen age: 19
app.get('/get',(req,res) => {
res.send(req.query);
});

// 处理POST请求,返回值为POST请求的内容
app.post('/post',(req,res) => {
res.send(req.body);
});

// 监听端口
app.listen(3000,'localhost');

// 控制台提示输出
console.log("服务器启动成功!");

之后访问http://localhost:3000/fontend.html,打开浏览器控制台,就可以看到输出内容了。

在上面的POST请求中,给出的是JSON处理的方法,如果不想用JSON处理,也可以向GET请求那样使用拼接来实现。需要注意的是,GET请求内容只能用拼接来构造,POST请求内容可以用拼接或者JSON构造。

1
2
3
4
5
6
7
var xhr = new XMLHttpRequest();
xhr.open("POST","http://localhost:3000/post");
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.send("name=zhangsan&age=18");
xhr.onload = function() {
console.log(JSON.parse(xhr.responseText));
}

Ajax异常

  1. 网络通常,服务端能接收请求,服务端返回结果不是预期结果,使用xhr.status获取http状态码,进行处理
  2. 网络畅通,服务端没有接收到请求,返回404状态码,检查请求地址是否错误
  3. 网络畅通,服务器端接受请求,服务器返回500状态码,服务器后端异常
  4. 网络中断,请求无法发送到服务器端,会触发xhr的onerror事件

如何让后端返回非200状态码?
res.status(400).send('error');

前端ajax如何判断状态码?

1
2
3
if(xhr.status != 200){
alert("request & response error")
}

如何设置网络中断的环境?
浏览器模拟网络中断

网络中断情况下不会触发onload事件,但是会触发onerror事件:

1
2
3
xhr.onerror = function() {
alert("network err,can't send request");
}

Ajax and RESTful API

RESTful API 提供了一套增删改查的规范,将资源和动作抽象,划清资源、方法和URL的界限。资源即是操作集合的名字,包含在url内,动作是HTTP的请求方法,包括GET、POST、PUT、DELETE,使用这些方法来实现对数据的增删改查。

传统请求可能这样来删除id=1的服务器的:

http://www.example.com/svc/remove/1
http://www.example.com/svc/delete/1
http://www.example.com/server/shanchu/1

RESTful API 是这样实现的:

curl -d ‘{id: 1}’ -XDELETE http://www.example.com/svc

具体代码如下:

后端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
const express = require('express');
const app = express();
app.use(express.static(path.join(__dirname,'public')));

app.get("/users",(req,res) => {
res.send({
event: "获取当前用户列表"
});
});

app.get("/users/:id",(req,res) => {
// 获取url里的字段
const id = req.params.id
res.send({
// 注意这里用的是``,不是单引号
event: `获取id为${id}的用户信息`
})
});

app.delete("/users/:id",(req,res) => {
const id = req.params.id
res.send({
event: `删除id为${id}的用户信息`
})
});

app.put("/users/:id",(req,res) => {
const id = req.params.id
res.send({
event: `修改id为${id}的用户信息`
})
});

app.listen(3000,'localhost');
console.log("服务器启动成功!");

前端html(这里只给出js)

1
2
3
4
5
6
7
8
9
10
<scirpt>
// 删除id=1的服务器信息
var xhr = new XMLHttpRequest();
// DELETE可以换成其他方法
xhr.open("DELETE",'http://localhost:3000/users/1');
xhr.send();
xhr.onload = function(){
console.log(xhr.responseText);
}
</script>