1. 初识Ajax
./public/Ajax.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<span>我是客户端</span>
<!--
配合server.js使用:
首先,这个代码得先执行起来, 才能发送请求, 那么这个代码怎么执行呢???
是直接按照之前的那样直接运行html呢???还是????
===> 首先这是一个静态资源文件, 我们在服务端中可以给客户端响应静态资源;
首先开启服务器, 然后再在浏览器中输入http://localhost:3000/client.html;
此时服务器就会执行相应的静态资源文件, 也即是client.html,
然后就会执行client.html中的js代码,接下来就清晰了!!!
===> 所以说,启动服务器,然后输入localhost:3000/client.html即可!!!
-->
<script>
// 1. 创建Ajax对象
var xhr = new XMLHttpRequest();
// 2. 告诉Ajax对象向哪发送请求以及请求的方式
xhr.open('get','http://localhost:3000/first');
// 3. 发送请求
xhr.send();
// 4. 获取服务端响应给客户端的数据(` 当Ajax接收完服务端的响应之后,就会xhr的触发onload事件 `)
xhr.onload = function() {
console.log(xhr.responseText);
}
</script>
</body>
</html>
server.js
/*
传统网站中存在的问题:
网速慢的情况下,页面加载时间长,用户只能等待;
表单提交后,如果一项内容不合格,需要重新填写所有表单内容;
页面跳转,重新加载页面,造成资源浪费,增加用户等待时间;
*/
/*
AJax:
它是浏览器提供的一套方法,```可以实现页面无刷新更新数据```,提高用户浏览网站应用的体验;
应用场景:
.页面上拉加载更多数据;
.列表数据无刷新分页;
.表单项离开焦点数据验证;
.搜索框提示文字下拉列表;
*/
/*
Ajax的运行环境
Ajax技术需要运行在网站环境中才能生效,
当前课程会使用Node创建的服务器作为网站服务器;
*/
const express = require('express');
const path = require('path');
const app = express();
//静态资源的访问
app.use(express.static(path.join(__dirname, 'public')));
app.get('/first', (req, res) => {
res.send('Hello, Ajax!');
});
app.listen(3000, function () {
console.log('server is listening ... ... ');
});
2. 请求方式
./public/01_返回JSON对象.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--
在http请求与响应的过程中,无论是请求参数还是响应内容,
如果是对象类型,最终都会被转换为对象字符串进行传输。
res.send({"name": "ZhangSan"});
如下, app响应的是JSON对象, 但是会被转换为字符串进行传输,
所以在客户端接收的实际上"{"name": "ZhangSan"}",这是一个字符串,
所以在客户端要进行转换, 将其转换成json对象!!!
-->
</head>
<body>
<script>
var xhr = new XMLHttpRequest();
xhr.open('get','http://localhost:3000/responseData');
xhr.send();
xhr.onload = function() {
// console.log(xhr.responseText); // "{"name": "ZhangSan"}": JSON字符串
// console.log(typeof(xhr.responseText)); // string
var obj = JSON.parse(xhr.responseText); // string => JSON对象 {"name": "ZhangSan"}
var str = '<h1>' + obj.name + '</h1>';
document.body.innerHTML = str;
};
</script>
</body>
</html>
./public/02_传递get请求参数.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p>
<input type="text" id="username">
</p>
<p>
<input type="text" id="age">
</p>
<p>
<input type="button" value="提交" id="btn">
</p>
<script>
var btn = document.getElementById('btn');
var username = document.getElementById('username');
var age = document.getElementById('age');
btn.onclick = function() {
var xhr = new XMLHttpRequest();
var nameValue = username.value;
var ageValue = age.value;
var params = 'username=' + nameValue + '&age=' + ageValue; // Ajax:自己拼接请求参数
xhr.open('get', 'http://localhost:3000/getArgs?' + params);
xhr.send();
xhr.onload = function() {
console.log(xhr.responseText);
}
}
</script>
</body>
</html>
./public/03_传递post请求参数.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--
post请求必须在请求头中明确设置请求数据的格式类型; (setRequestHeader(...))
-->
</head>
<body>
<p>
<input type="text" id="username">
</p>
<p>
<input type="text" id="age">
</p>
<p>
<input type="button" value="提交" id="btn">
</p>
<script>
var btn = document.getElementById('btn');
var username = document.getElementById('username');
var age = document.getElementById('age');
btn.onclick = function () {
var xhr = new XMLHttpRequest();
var nameValue = username.value;
var ageValue = age.value;
var params = 'username=' + nameValue + '&age=' + ageValue;
xhr.open('post', 'http://localhost:3000/postArgs');
// 设置请求参数格式的类型(post请求必须设置请求头,get请求不需要)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// post请求参数一定要放在send中
xhr.send(params);
xhr.onload = function () {
console.log(xhr.responseText);
}
}
</script>
</body>
</html>
./public/04_请求参数的格式.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--
请求参数的格式:
'Content-Type': 'application/x-www-form-urlencoded' name='zhang'&age=23
'Content-Type': 'application/json' { name: 'zhang', age: 23 }
-->
<!--
客户端传递不同格式的请求参数, 那么服务器端接收方式也是不一样的!
-->
</head>
<body>
<script>
var xhr = new XMLHttpRequest();
xhr.open('post', 'http://localhost:3000/json');
xhr.setRequestHeader('Content-Type', 'application/json'); // 客户端向服务器传递的请求参数格式为json
xhr.send(JSON.stringify({"name": "Zhang", "age": 14})); // 将JSON对象转换为json字符串(请求参数必须以字符串的形式进行传递)
xhr.onload = function() {
console.log(xhr.responseText);
};
/*
注意:get请求参数是不能提交JSON对象数据格式的, (只能提交application/x-www-form-urlencoded格式的数据)
传统网站的表单提交也是不支持JSON对象数据格式的!
*/
</script>
</body>
</html>
2.1
参数传递
server.js
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const app = express();
/*
服务器端响应的数据格式:
在真实的项目中,服务器端大多数情况下会以JSON对象作为响应数据的格式;
当客户端拿到响应数据时,要将JSON数据和HTML字符串进行拼接;
然后将拼接的结果展示在页面中。
*/
/*
在http请求与响应的过程中,无论是请求参数还是响应内容,
如果是对象类型,最终都会被转换为对象字符串进行传输。
res.send({"name": "ZhangSan"});
如上, 服务器响应的是JSON对象, 但是会被转换为字符串进行传输,
所以在客户端接收的实际上"{"name": "ZhangSan"}",这是一个字符串,所以在客户端要进行转换, 将其转换成JSON对象!!!
*/
/*
1.application/x-www-form-urlencoded :
name=Zhang&age=23
===>
app.use(bodyParser .urlencoded());
```表示解析application/x-www-form-urlencoded格式的数据```
2.application/json :
{name: 'Zhang', age: '34', sex: 'man'}
===>
app.use(bodyParser.json());
```表示解析application/json格式的数据```
在请求头中指定Content-Type属性的值是application/x-www-form-urlencoded ,则是告诉服务器当前请求参数的格式是x-www-form-urlencoded(name=Zhang&age=23)!
在请求头中指定Content-Type属性的值是application/json,则是告诉服务器当前请求参数的格式是json({ name: 'Zhang', age:'23'})!
*/
//静态资源访问
app.use(express.static(path.join(__dirname, 'public')));
// 解析application/x-www-form-urlencoded数据格式(name='Zhang'&age=23)
app.use(bodyParser .urlencoded());
// responseData路由
app.get('/responseData', (req, res) => {
res.send({"name": "ZhangSan"}); // 响应JSON数据给客户端
});
// getArgs路由
app.get('/getArgs', (req, res) => {
res.send(req.query); // 响应给客户端
});
// postArgs路由
app.post('/postArgs', (req, res) => {
res.send(req.body);
});
app.use(bodyParser .json()); // 解析客户端发来application/json数据格式
// json路由
app.post('/json', (req, res) => {
res.send(req.body);
});
app.listen(3000, () => {
console.log(`Server started on port`);
});
2.2
Ajax状态码
test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--
获取服务器端响应客户端数据的另一种方式:
获取服务器端的响应:
Ajax状态码: (xhr.readyState)
在创建ajax对象,配置ajax对象,发送请求,以及接收完服务器端响应数据,
这个过程中的每一个步骤都会对应一个数值,这个数值就是ajax状态码。
0:请求未初始化【还没有调用open()】;
1:请求已经建立,但是还没有发送【还没有调用send()】;
2:请求已经发送;
3:请求正在处理中,通常响应中已经有部分数据可以用了;
4:响应已经完成,可以获取并使用服务器的响应了;
5:
xhr.readyState // 获取Ajax状态码
``````onreadystateChange事件: 当Ajax状态码发生变化时将自动触发该事件!``````
-->
</head>
<body>
<script>
var xhr = new XMLHttpRequest();
console.log(xhr.readyState); // 0 => 创建了Ajax对象
xhr.open('get', 'http://localhost:3000/readyState');
console.log(xhr.readyState); // 1 => 已经对AJax对象进行了配置
xhr.onreadystatechange = function () {
console.log(xhr.readyState);
// 2 => 请求已经发送
// 3 => 已经接收到服务端的部分数据了
// 4 => 服务端的响应数据已经接受完成
// 服务端的响应完成
if (xhr.readyState == 4) {
console.log(xhr.responseText);
}
}
xhr.send();
</script>
</body>
</html>
app.js
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static(__dirname));
app.get('/readyState', (req, res) => {
res.send('请求结束');
})
app.listen(3000, () => {
console.log(`Server started on port`);
});
3. 错误处理
3.1
init
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!--
Ajax错误处理: 当请求结果不是预期结果时,应该如何处理???
1. 网络畅通, 服务器端能接受到请求,服务器端返回的结果不是预期结果;
=> 可以判断服务器端返回的状态码,分别将进行处理,通过xhr.status获取http状态码;
2. 网络畅通,服务器端没有接受到请求,返回404状态码。
=> ```检查请求地址是否错误```;
3. 网络畅通,服务器端能接受请求,服务器端返回400状态码。
=> ```服务器端错误(比如返回了一个不存在的变量)```,找后端程序员进行沟通;
4. 网络中断,请求无法发送到服务器端;
=> 不会触发onload事件;
=> 会触发onerror事件, 在onerror事件处理函数中对错误进行处理;
-->
</body>
</html>
3.2
模拟测试
error.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--
1. xhr.readyState: Ajax状态码(0, 1, 2, 3, 4);
2. xhr.status: http状态码(200, 400, 404, 500 ...);
-->
</head>
<body>
<button id="btn">发送Ajax请求</button>
<script>
var btn = document.getElementById('btn');
btn.onclick = function () {
var xhr = new XMLHttpRequest();
xhr.open('get', 'http://localhost:3000/error');
xhr.send();
xhr.onload = function () {
console.log(xhr.responseText);
if(xhr.status == 400) {
alert('请求出错');
}
};
// 网络中断触发onerror事件
// 模拟, offline, 然后点击按钮, 将触发此事件
xhr.onerror = function() {
alert('网络中断');
}
}
</script>
</body>
</html>
app.js
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static(__dirname));
app.get('/error',(req, res) => {
// console.log(abc); // 此时服务端出错(输出了一个不存在的变量), 此时将返回400状态码给客户端
res.status(400).send('not ok');
});
app.listen(3000);
console.log('app start successful!');
4. Ajax缓存
4.1
init
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!--
低版本IE浏览器的缓存问题:
在低版本的IE浏览器中,Ajax请求有严重的缓存问题,即```在请求地址不发生变化的情况下```,
只有第一次请求会真正发送到服务器,后续的请求都会从浏览器的缓存中获取;
即使服务器端的数据更新了,客户端依然拿到的是缓存中的旧数据;
解决方案:
在请求地址的后面添加请求参数,保证每一次请求中的请求参数的值不同;
xhr.open('get', 'http://localhost:3000/cache?x=' + Math.random());
-->
</body>
</html>
4.2
测试
cache.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--
的确: 在IE浏览器中确实存在这种情况!!!
在谷歌,360中倒是没有这种情况!
-->
<!--
解决方案:在请求地址的后面加请求参数,保证每一次请求中的请求参数的值不相同。
-->
</head>
<body>
<button id="btn">缓存</button>
<script>
var btn = document.getElementById('btn');
btn.onclick = function () {
var xhr = new XMLHttpRequest();
xhr.open('get', 'http://localhost:3000/cache');
// xhr.open('get', 'http://localhost:3000/cache?x=' + Math.random());
xhr.send();
xhr.onreadystatechange = function () {
if(xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
};
}
</script>
</body>
</html>
app.js
const express = require('express');
const path = require('path');
const fs = require('fs');
const server = express();
server.use(express.static(__dirname));
server.get('/cache', (req, res) => {
fs.readFile('./test.txt',(err, result)=>{
res.send(result);
});
});
server.listen(3000);
console.log('server start successful!');
下图中的IE浏览器就会出现此问题
。
好,采用上述的解决方案去解决该问题,结果如下图所示。
5. Ajax异步编程
init.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!--
异步:
落实到代码上, 就是异步代码虽然也需要花时间去执行, 但程序不会等待异步代码执行完后再继续执行后续代码,
而是直接执行后续代码; 当后续代码执行完成后再回过头看异步代码是否返回结果, 如果已有返回结果,
再调用事先准备好的回调函数处理异步代码执行的结果;
Ajax请求属于异步代码;
-->
<script>
var xhr = new XMLHttpRequest();
xhr.open('get','http://localhost:3000/first');
xhr.send();
xhr.onload = function() {
console.log('222222222');
console.log(xhr.responseText);
}
console.log('11111111');
/*
很明显:
11111111
222222222
22 Ok!
*/
</script>
</body>
</html>
app.js
const express = require('express');
const path = require('path');
const server = express();
server.use(express.static(__dirname));
server.get('/first', (req, res) => {
res.send('Ok!');
});
server.listen(3000, () => {
console.log('server start successful!');
});
6. Ajax封装
test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--
Ajax函数封装:
问题:发送一次请求代码过多,发送多次请求代码冗余且重复!
解决方案:将请求代码封装到函数中,发送请求时调用函数即可!
-->
</head>
<body>
<script>
function ajax(options) {
// 存储的是默认值
var defaults = {
type: 'get',
url: '',
data: {},
header: {
'Content-Type': 'application/x-www-form-urlencoded'
},
success: function() { },
error: function() { }
}
Object.assign(defaults, options);
//
var xhr = new XMLHttpRequest();
// 拼接get请求参数(name=Zhang&age=23))
var params = '';
for(var attr in defaults.data) {
params += attr + '=' + defaults.data[attr] + '&';
}
params = params.substring(0, params.length - 1); // name=Zhang&age=20&,去掉最后一个&
// 判断请求方式
if(defaults.type == 'get') {
defaults.url = defaults.url + '?' + params;
}
//
xhr.open(defaults.type, defaults.url); // 请求方式, 请求地址
//
if(defaults.type == 'post') {
var contentType = defaults.header['Content-Type']; // 请求参数类型
xhr.setRequestHeader('Content-Type', contentType);
if(contentType == 'application/json') {
xhr.send(JSON.stringify(defaults.data));
} else {
xhr.send(params);
}
} else {
xhr.send();
}
//
xhr.onload = function() {
// console.log(xhr.getResponseHeader('Content-Type')); //获取服务器端响应头的数据类型
var responseText = xhr.responseText;
if((xhr.getResponseHeader('Content-Type')).includes('application/json')) {
responseText = JSON.parse(xhr.responseText);
}
if(xhr.status == 200) {
defaults.success(responseText, xhr);
} else {
defaults.error(responseText, xhr);
}
};
}
// 调用函数
ajax({
type: 'post',
url: 'http://localhost:3000/index',
success: function(data, xhr) {
console.log('success()');
console.log(data);
console.log(xhr);
}
});
/*
请求参数要考虑的问题:
1.请求参数位置的问题
将请求参数传递到Ajax函数内部,在函数内部根据请求方式的不同将请求参数放置在不同的位置 ;
get 请求参数放在请求地址的后面; http://localhost/index?name=zhang&age=24
post 请求参数直接放在send方法中;
2. 格式问题
application/x-www-form-urlencoded (name=Zhang&age=24)
application/json ({"name": "Zhang", "age": "25"})
*/
</script>
</body>
</html>
app.js
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static(__dirname));
app.get('/index', (req, res) => {
res.send('Ajax函数封装'); /* 此时响应头的数据类型是: text/html */
// res.send({"name": "Wang", "age": 23}); /* 此时响应头的数据类型是:application/json */
});
app.post('/index',(req, res) => {
// res.send("HelloWorld!!!!!!");
res.send({"name": "Wang", "age": 23});
});
app.listen(3000, () => {
console.log(`Server started on port`);
});
7. 浏览器模板引擎
init.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 将模板引擎引入到当前页面 -->
<script src="./template-web.js"></script>
</head>
<body>
<div id="container"></div>
<script type="text/html" id="tp1">
<h1>{{username}} {{age}}</h1>
</script>
<script type="text/javascript">
/* 以下操作相当于是将{username: ..., age: ...}对象中的值给渲染到tp1中相应的位置; 然后将渲染后的数据整体返回; */
var html = template
('tp1', {username: 'ZhangSan', age: 32});
// console.log(html); // <h1>Zhang 32</h1>
document.getElementById('container').innerHTML = html;
</script>
</body>
</html>
8. Ajax案例
8.1
gitee地址
https://gitee.com/studyCodingEx/studys
8.2
效果演示