Ajax
# Ajax
Ajax基础
传统网站中存在的问题:
-
网速慢的情况下,页面加载时间长,用户只能等待
-
表单提交后,如果一项内容不合格,需要重新填写所有表单内容
-
页面跳转,重新加载页面,造成资源浪费,增加用户等待时间
Ajax概述:
它是浏览器提供的一套方法,可以实现页面无刷新更新数据,提高用户浏览网站应用的体验
Ajax的运行环境
Ajax 技术需要运行在网站环境中才能生效,当前课程会使用Node创建的服务器作为网站服务器。
Ajax运行原理及实现
Ajax 相当于浏览器发送请求与接收响应的代理人,以实现在不影响用户浏览页面的情况下,局部更新页面数据,从而提高用户体验。
实现步骤
服务器端响应的数据格式
在真实的项目中,服务器端大多数情况下会以 JSON 对象作为响应数据的格式。当客户端拿到响应数据时,要将 JSON 数据和 HTML 字符串进行拼接,然后将拼接的结果展示在页面中。
在 http 请求与响应的过程中,无论是请求参数还是响应内容,如果是对象类型,最终都会被转换为对象字符串进行传输。
请求参数传递
传统网站表单提交:
请求报文
在 HTTP 请求和响应的过程中传递的数据块就叫报文,包括要传送的数据和一些附加信息,这些数据和信息要遵守规定好的格式。
请求参数格式
获取服务器端的响应
Ajax状态码:
在创建ajax对象,配置ajax对象,发送请求,以及接收完服务器端响应数据,这个过程中的每一个步骤都会对应一个数值,这个数值就是ajax状态码。
- 0:请求未初始化(还没有调用open())
- 1:请求已经建立,但是还没有发送(还没有调用send())
- 2:请求已经发送
- 3:请求正在处理中,通常响应中已经有部分数据可以用了
- 4:响应已经完成,可以获取并使用服务器的响应了
获取服务器端的响应
onreadystatechange 事件
当 Ajax 状态码发生变化时将自动触发该事件。
在事件处理函数中可以获取 Ajax 状态码并对其进行判断,当状态码为 4 时就可以通过 xhr.responseText 获取服务器端的响应数据了。
两种获取服务器端响应方式的区别:
Ajax错误处理
低版本IE浏览器的缓存问题
问题:在低版本的 IE 浏览器中,Ajax 请求有严重的缓存问题,即在请求地址不发生变化的情况下,只有第一次请求会真正发送到服务器端,后续的请求都会从浏览器的缓存中获取结果。即使服务器端的数据更新了,客户端依然拿到的是缓存中的旧数据。
解决方案:在请求地址的后面加请求参数,保证每一次请求中的请求参数的值不相同
Ajax封装
问题:发送一次请求代码过多,发送多次请求代码冗余且重复。
解决方案:将请求代码封装到函数中,发请求时调用函数即可。
请求参数要考虑的问题:
1.请求参数位置的问题:
将请求参数传递到ajax函数内部, 在函数内部根据请求方式的不同将请求参数放置在不同的位置
get 放在请求地址的后面
post 放在send方法中
2.请求参数格式的问题:
application/x-www-form-urlencoded => 参数名称=参数值&参数名称=参数值 => name=zhangsan&age=20
application/json => {name: 'zhangsan', age: 20}
1.传递对象数据类型对于函数的调用者更加友好 2.在函数内部对象数据类型转换为字符串数据类型更加方便
代码如下:
function ajax (options) {
// 存储的是默认值
var defaults = {
type: 'get',
url: '',
data: {},
header: {
'Content-Type': 'application/x-www-form-urlencoded'
},
success: function () {},
error: function () {}
};
// 使用options对象中的属性覆盖defaults对象中的属性
Object.assign(defaults, options);
// 创建ajax对象
var xhr = new XMLHttpRequest();
// 拼接请求参数的变量
var params = '';
// 循环用户传递进来的对象格式参数
for (var attr in defaults.data) {
// 将参数转换为字符串格式
params += attr + '=' + defaults.data[attr] + '&';
}
// 将参数最后面的&截取掉
// 将截取的结果重新赋值给params变量
params = params.substr(0, - 1);
// 判断请求方式
if (defaults.type == 'get') {
defaults.url = defaults.url + '?' + params;
}
// 配置ajax对象
xhr.open(defaults.type, defaults.url);
// 如果请求方式为post
if (defaults.type == 'post') {
// 用户希望的向服务器端传递的请求参数的类型
var contentType = defaults.header['Content-Type']
// 设置请求参数格式的类型
xhr.setRequestHeader('Content-Type', contentType);
// 判断用户希望的请求参数格式的类型
// 如果类型为json
if (contentType == 'application/json') {
// 向服务器端传递json数据格式的参数
xhr.send(JSON.stringify(defaults.data))
}else {
// 向服务器端传递普通类型的请求参数
xhr.send(params);
}
}else {
// 否则get发送请求
xhr.send();
}
// 监听xhr对象下面的onload事件
// 当xhr对象接收完响应数据后触发
xhr.onload = function () {
// xhr.getResponseHeader()
// 获取响应头中的数据
var contentType = xhr.getResponseHeader('Content-Type');
// 服务器端返回的数据
var responseText = xhr.responseText;
// 如果响应类型中包含applicaition/json
if (contentType.includes('application/json')) {
// 将json字符串转换为json对象
responseText = JSON.parse(responseText)
}
// 当http状态码等于200的时候
if (xhr.status == 200) {
// 请求成功 调用处理成功情况的函数
defaults.success(responseText, xhr);
}else {
// 请求失败 调用处理失败情况的函数
defaults.error(responseText, xhr);
}
}
}
ajax({
type: 'post',
// 请求地址
url: 'http://localhost:3000/responseData',
success: function (data) {
console.log('这里是success函数');
console.log(data)
}
})
模板引擎
art-template使用步骤
art-template语法格式
标准语法
{{value}}
{{data.key}}
{{data['key']}}
{{a ? b : c}}
{{a || b}}
{{a + b}}
原始语法
<%= value %>
<%= data.key %>
<%= data['key'] %>
<%= a ? b : c %>
<%= a || b %>
<%= a + b %>
循环语法:
{{each target}}
{{$index}} {{$value}}
{{/each}}
<% for(var i = 0; i < target.length; i++){ %> <%= i %> <%= target[i] %><% } %>
案例
验证邮箱地址唯一性
代码:
// 获取页面中的元素
var emailInp = document.getElementById('email');
var info = document.getElementById('info');
// 当文本框离开焦点以后
emailInp.onblur = function () {
// 获取用户输入的邮箱地址
var email = this.value;
// 验证邮箱地址的正则表达式
var reg = /^[A-Za-z\d]+([-_.][A-Za-z\d]+)*@([A-Za-z\d]+[-.])+[A-Za-z\d]{2,4}$/;
// 如果用户输入的邮箱地址不符合规则
if (!reg.test(email)) {
// 给出用户提示
info.innerHTML = '请输入符合规则的邮箱地址';
// 让提示信息显示为错误提示信息的样式
info.className = 'bg-danger';
// 阻止程序向下执行
return;
}
// 向服务器端发送请求
ajax({
type: 'get',
url: 'http://localhost:3000/verifyEmailAdress',
data: {
email: email
},
success: function (result) {
console.log(result);
info.innerHTML = result.message;
info.className = 'bg-success';
},
error: function (result) {
console.log(result)
info.innerHTML = result.message;
info.className = 'bg-danger';
}
});
}
搜索框内容自动提示
代码:
<script type="text/html" id="tpl">
{{each result}}
<li class="list-group-item">{{$value}}</li>
{{/each}}
</script>
<script>
var searchInp = document.getElementById('search');
var listBox = document.getElementById('list-box');
var timer = null;
// 当用户在文本框中输入的时候触发
searchInp.oninput = function () {
// 清除上一次开启的定时器
clearTimeout(timer);
// 获取用户输入的内容
var key = this.value;
if (key.trim().length == 0) {
listBox.style.display = 'none';
return;
}
// 开启定时器 让请求延迟发送
timer = setTimeout(function () {
ajax({
type: 'get',
url: 'http://localhost:3000/searchAutoPrompt',
data: {
key: key
},
success: function (result) {
// 使用模板引擎拼接字符串
var html = template('tpl', {result: result});
// 将拼接好的字符串显示在页面中
listBox.innerHTML = html;
// 显示ul容器
listBox.style.display = 'block';
}
})
}, 800)
}
</script>
判断输入是否为空:
var textValue = text.replace(/(^\s*)|(\s*$)/g, ""); //替换输入内容当中所有的空字符,包括全角空格,半
if(textValue==null || textValue=="") {
alert("输入的内容为空,无法查询!");
return false;
}
省市区联动
模板:
<!-- 省份模板 -->
<script type="text/html" id="provinceTpl">
<option>请选择省份</option>
{{each province}}
<option value="{{$value.id}}">{{$value.name}}</option>
{{/each}}
</script>
<!-- 城市模板 -->
<script type="text/html" id="cityTpl">
<option>请选择城市</option>
{{each city}}
<option value="{{$value.id}}">{{$value.name}}</option>
{{/each}}
</script>
<!-- 县城模板 -->
<script type="text/html" id="areaTpl">
<option>请选择县城</option>
{{each area}}
<option value="{{$value.id}}">{{$value.name}}</option>
{{/each}}
</script>
js代码:
<script>
// 获取省市区下拉框元素
var province = document.getElementById('province');
var city = document.getElementById('city');
var area = document.getElementById('area');
// 获取省份信息
ajax({
type: 'get',
url: 'http://localhost:3000/province',
success: function (data) {
// 将服务器端返回的数据和html进行拼接
var html = template('provinceTpl', {province: data});
// 将拼接好的html字符串显示在页面中
province.innerHTML = html;
}
});
// 为省份的下拉框添加值改变事件
province.onchange = function () {
// 获取省份id
var pid = this.value;
// 清空县城下拉框中的数据
var html = template('areaTpl', {area: []});
area.innerHTML = html;
// 根据省份id获取城市信息
ajax({
type: 'get',
url: '/cities',
data: {
id: pid
},
success: function (data) {
var html = template('cityTpl', {city: data});
city.innerHTML = html;
}
})
};
// 当用户选择城市的时候
city.onchange = function () {
// 获取城市id
var cid = this.value;
// 根据城市id获取县城信息
ajax({
type: 'get',
url: 'http://localhost:3000/areas',
data: {
id: cid
},
success: function(data) {
var html = template('areaTpl', {area: data});
area.innerHTML = html;
}
})
}
</script>
FormData
FormData对象的作用
FormData对象的使用
var btn = document.getElementById('btn');
// 获取表单
var form = document.getElementById('form');
// 为按钮添加点击事件
btn.onclick = function () {
// 将普通的html表单转换为表单对象
var formData = new FormData(form);
// 创建ajax对象
var xhr = new XMLHttpRequest();
// 对ajax对象进行配置
xhr.open('post', 'http://localhost:3000/formData');
// 发送ajax请求
xhr.send(formData);
// 监听xhr对象下面的onload事件
xhr.onload = function () {
// 对象http状态码进行判断
if (xhr.status == 200) {
console.log(xhr.responseText);
}
}
}
服务器端使用formidable解析formdata对象数据:
app.post('/formData', (req, res) => {
// 创建formidable表单解析对象
const form = new formidable.IncomingForm();
// 解析客户端传递过来的FormData对象
form.parse(req, (err, fields, files) => {
res.send(fields);
});
FormData对象的实例方法
FormData二进制文件上传
FormData文件上传进度展示
FormData文件上传图片即时预览
服务器端:
app.post('/upload', (req, res) => {
// 创建formidable表单解析对象
const form = new formidable.IncomingForm();
// 设置客户端上传文件的存储路径
form.uploadDir = path.join(__dirname, 'public', 'uploads');
// 保留上传文件的后缀名字
form.keepExtensions = true;
// 解析客户端传递过来的FormData对象
form.parse(req, (err, fields, files) => {
// 将客户端传递过来的文件地址响应到客户端
res.send({
path: files.attrName.path.split('public')[1]
});
});
});
客户端完整:
// 获取文件选择控件
var file = document.getElementById('file');
// 获取进度条元素
var bar = document.getElementById('bar');
// 获取图片容器
var box = document.getElementById('box');
// 为文件选择控件添加onchanges事件
// 在用户选择文件时触发
file.onchange = function () {
// 创建空的formData表单对象
var formData = new FormData();
// 将用户选择的文件追加到formData表单对象中
formData.append('attrName', this.files[0]);
// 创建ajax对象
var xhr = new XMLHttpRequest();
// 对ajax对象进行配置
xhr.open('post', 'http://localhost:3000/upload');
// 在文件上传的过程中持续触发
xhr.upload.onprogress = function (e) {
// ev.loaded 文件已经上传了多少
// ev.total 上传文件的总大小
var result = (e.loaded / e.total) * 100 + '%';
// 设置进度条的宽度
bar.style.width = result;
// 将百分比显示在进度条中
bar.innerHTML = result;
}
// 发送ajax请求
xhr.send(formData);
// 监听服务器端响应给客户端的数据
xhr.onload = function () {
// 如果服务器端返回的http状态码为200
// 说明请求是成功的
if (xhr.status == 200) {
// 将服务器端返回的数据显示在控制台中
var result = JSON.parse(xhr.responseText);
// 动态创建img标签
var img = document.createElement('img');
// 给图片标签设置src属性
img.src = result.path;
// 当图片加载完成以后
img.onload = function () {
// 将图片显示在页面中
box.appendChild(img);
}
}
}
}
同源策略
jsonp
同源策略是浏览器的一种安全策略,所谓同源是指域名,协议,端口完全相同,只有同源的地址才可以相互通过AJAX 的方式请求。
同源或者不同源说的是两个地址之间的关系,不同源地址之间请求我们称之为跨域请求
什么是同源?例如:http://www.example.com/detail.html 与一下地址对比
解决方式一:jsonp
客户端发送jsonp
// 获取按钮
var btn1 = document.getElementById('btn1');
// 为按钮添加点击事件
btn1.onclick = function () {
jsonp({
// 请求地址
url: 'http://localhost:3001/better',
data: {
name: 'lisi',
age: 30
},
success: function (data) {
console.log(123)
console.log(data)
}
})
}
function jsonp (options) {
// 动态创建script标签
var script = document.createElement('script');
// 拼接字符串的变量
var params = '';
for (var attr in options.data) {
params += '&' + attr + '=' + options.data[attr];
}
// myJsonp0124741
var fnName = 'myJsonp' + Math.random().toString().replace('.', '');
// 它已经不是一个全局函数了
// 我们要想办法将它变成全局函数
window[fnName] = options.success;
// 为script标签添加src属性
script.src = options.url + '?callback=' + fnName + params;
// 将script标签追加到页面中
document.body.appendChild(script);
// 为script标签添加onload事件
script.onload = function () {
document.body.removeChild(script);
}
}
服务器端:
app.get('/better', (req, res) => {
接收客户端传递过来的函数的名称
const fnName = req.query.callback;
将函数名称对应的函数调用代码返回给客户端
const data = JSON.stringify({name: "张三"});
const result = fnName + '('+ data +')';
setTimeout(() => {
res.send(result);
}, 1000)
//或者直接用:
res.jsonp({name: 'lisi', age: 20});
});
CORS跨域资源共享
CORS:全称为 Cross-origin resource sharing,即跨域资源共享,它允许浏览器向跨域服务器发送 Ajax 请求,克服了 Ajax 只能同源使用的限制。
Node 服务器端设置响应头示例代码:
app.get('/cross', (req, res) => {
res.header('Access-Control-Allow-Origin', 'http://localhost:3000')
// 2.允许客户端使用哪些请求方法访问我
res.header('Access-Control-Allow-Methods', 'get,post')
res.send('ok')
});
或在全局下拦截所有请求:
app.use((req, res, next) => {
// 1.允许哪些客户端访问我
// * 代表允许所有的客户端访问我
// 注意:如果跨域请求中涉及到cookie信息传递,值不可以为*号 比如是具体的域名信息
res.header('Access-Control-Allow-Origin', 'http://localhost:3000')
// 2.允许客户端使用哪些请求方法访问我
res.header('Access-Control-Allow-Methods', 'get,post')
// 允许客户端发送跨域请求时携带cookie信息
res.header('Access-Control-Allow-Credentials', true);
next();
});
访问非同源数据服务器端解决方案
同源政策是浏览器给予Ajax技术的限制,服务器端是不存在同源政策限制。
向我自己A服务器/server发送ajax请求,再通过A服务器对B服务器/cross发送请求获取数据
A客户端代码:
// 获取按钮
var btn = document.getElementById('btn');
// 为按钮添加点击事件
btn.onclick = function () {
ajax({
type: 'get',
url: 'http://localhost:3000/server',
success: function (data) {
console.log(data);
}
})
};
A服务器端代码:
// 向其他服务器端请求数据的模块
const request = require('request');
app.get('/server', (req, res) => {
request('http://localhost:3001/cross', (err, response, body) => {
res.send(body);
})
});
B服务器端代码:
app.get('/cross', (req, res) => {
res.send('ok')
});
cookie实现跨域登录功能
在使用Ajax技术发送跨域请求时,默认情况下不会在请求中携带cookie信息。
withCredentials
属性
withCredentials:指定在涉及到跨域请求时,是否携带cookie信息,默认值为false
A服务器下的客户端对B服务器端发送登录请求,并且携带cookie
A客户端代码:
loginBtn.onclick = function () {
// 将html表单转换为formData表单对象
var formData = new FormData(loginForm);
// 创建ajax对象
var xhr = new XMLHttpRequest();
// 对ajax对象进行配置
xhr.open('post', 'http://localhost:3001/login');
// 当发送跨域请求时,携带cookie信息
xhr.withCredentials = true;
// 发送请求并传递请求参数
xhr.send(formData);
// 监听服务器端给予的响应内容
xhr.onload = function () {
console.log(xhr.responseText);
}
}
B服务器端:
app.post('/login', (req, res) => {
// 创建表单解析对象
var form = formidable.IncomingForm();
// 解析表单
form.parse(req, (err, fields, file) => {
// 接收客户端传递过来的用户名和密码
const { username, password } = fields;
// 用户名密码比对
if (username == 'itheima' && password == '123456') {
// 设置session
req.session.isLogin = true;
res.send({message: '登录成功'});
} else {
res.send({message: '登录失败, 用户名或密码错误'});
}
})
});
jQuery中的Ajax
$.ajax()
概述
发送Ajax请求:
注意:
- success函数response参数为服务器端返回的数据,方法内部会自动将json字符串转换为json对象
- data也可以为参数字符串的格式
- beforSend方法的return false可阻止发送Ajax
serialize
方法
作用:将表单中的数据自动拼接成字符串类型的参数
$('#form').on('submit', function () {
// 将表单内容拼接成字符串类型的参数
// username = '用户输入的内容' & password = 123456
var params = $('#form').serialize();
//封装函数将用户在表单里输入的内容转化成对象的形式
//{username:'用户输入的内容',password:'123456'}
//需要用到 serializeArray()方法
serializeObject($(this));
return false;
});
// 将表单中用户输入的内容转换为对象类型
function serializeObject (obj) {
var result = {};
// [{name: 'username', value: '用户输入的内容'}, {name: 'password', value: '123456'}]
var params = obj.serializeArray();
$.each(params, function (index, value) {
result[value.name] = value.value;
})
// 将处理的结果返回到函数外部方便进行处理 {username:'用户输入的内容',password:'123456'}
return result;
}
发送jsonp
请求
客户端:
function fn (response) {
console.log(response)
}
$('#btn').on('click', function () {
$.ajax({
url: '/jsonp',
// 向服务器端传递函数名字的参数名称
jsonp: 'cb',
jsonpCallback: 'fn',
// 代表现在要发送的是jsonp请求
dataType: 'jsonp'/*,
success: function (response)
console.log(response)
}*/
})
});
服务器端:
app.get('/jsonp', (req, res) => {
const cb = req.query.cb
const data = cb+"({name: 'zhaoliu'})"
res.send(data);
// res.jsonp({
// name: 'lisi',
// age:50
// })
});
- jsonp修改参数callback名称
- jsonpCallback修改函数名称
$.get、$.post
作用:$.get方法用于发送get请求,$.post方法用于发送post请求。
$.get('/base', 'name=zhangsan&age=30', function (response) {
console.log(response)
})
Todo案例
客户端:
<!-- 任务列表模板 -->
<script type="text/html" id="taskTpl">
{{each tasks}}
<li class="{{$value.completed ? 'completed' : ''}}">
<div class="view">
<input class="toggle" type="checkbox" {{$value.completed ? 'checked' : ''}}>
<label>{{$value.title}}</label>
<button class="destroy" data-id="{{$value._id}}"></button>
</div>
<input class="edit">
</li>
{{/each}}
</script>
<script type="text/javascript">
// 用于存放任务列表的数组
var taskAry = [];
// 选择任务列表容器
var taskBox = $('#todo-list');
// 添加任务的文本框
var taskInp = $('#task');
// 用于存储未完成任务数量的strong标签
var strong = $('#count');
// 当页面中有ajax请求发送时触发
$(document).on('ajaxStart', function () {
NProgress.start()
})
// 当页面中有ajax请求完成时触发
$(document).on('ajaxComplete', function () {
NProgress.done()
})
// 向服务器端发送请求 获取已经存在的任务
$.ajax({
url: '/todo/task',
type: 'get',
success: function (response) {
// 将已存在的任务存储在taskAry变量中
taskAry = response;
// 拼接字符串 将拼接好的字符串显示在页面中
render();
// 计算未完成任务数量
calcCount ()
}
})
// 获取文本框并且添加键盘抬起事件
taskInp.on('keyup', function (event) {
// 如果用户敲击的是回车键
if (event.keyCode == 13) {
// 判断用户是否在文本框中输入了任务名称
var taskName = $(this).val();
// 如果用户没有在文本框中输入内容
if (taskName.trim().length == 0) {
alert('请输入任务名称')
// 阻止代码向下执行
return;
}
// 向服务器端发送请求 添加任务
$.ajax({
type: 'post',
url: '/todo/addTask',
contentType: 'application/json',
data: JSON.stringify({title: taskName}),
success: function (response) {
// 将任务添加到任务列表中
taskAry.push(response);
// 拼接字符串 将拼接好的字符串显示在页面中
render();
// 清空文本框中的内容
taskInp.val('');
// 计算未完成任务数量
calcCount ()
}
})
}
});
// 拼接字符串 将拼接好的字符串显示在页面中
function render() {
// 字符串拼接
var html = template('taskTpl', {
tasks: taskAry
});
// 将拼接好的字符串显示在ul标签中
taskBox.html(html);
}
// 当用户点击删除按钮时触发ul标签身上的点击事件
taskBox.on('click', '.destroy', function () {
// 要删除的任务的id
var id = $(this).attr('data-id');
// 向服务器端发送请求删除 任务
$.ajax({
url: '/todo/deleteTask',
type: 'get',
data: {
_id: id
},
success: function (response) {
// 从任务数组中找到已经删除掉的任务的索引
var index = taskAry.findIndex(item => item._id == id);
// 将任务从数组中删除
taskAry.splice(index, 1);
// 重新将任务数组中的元素显示在页面中
render();
// 计算未完成任务数量
calcCount ()
}
})
});
// 当用户改变任务名称前面的复选框状态时触发
taskBox.on('change', '.toggle', function () {
// 代表复选框是否选中 true 选中 false 未选中的
const status = $(this).is(':checked');
// 当前点击任务的id
const id = $(this).siblings('button').attr('data-id');
// 向服务器端发送请求 更改任务状态
$.ajax({
type: 'post',
url: '/todo/modifyTask',
data: JSON.stringify({_id: id, completed: status}),
contentType: 'application/json',
success: function (response) {
// 将任务状态同步到任务数组中
var task = taskAry.find(item => item._id == id);
// 更改任务状态
task.completed = response.completed;
// 将数组中任务的最新状态更新到页面中
render();
// 计算未完成任务数量
calcCount ()
}
})
});
// 当双击事件名称的时候触发
taskBox.on('dblclick', 'label', function () {
// 让任务处于编辑状态
$(this).parent().parent().addClass('editing');
// 将任务名称显示在文本框中
$(this).parent().siblings('input').val($(this).text())
// 让文本框获取焦点
$(this).parent().siblings('input').focus();
})
// 当文本框离开焦点的时候
taskBox.on('blur', '.edit', function () {
// 最新的任务名称
var newTaskName = $(this).val();
// 编辑任务的id
var id = $(this).siblings().find('button').attr('data-id');
// 向服务器端发送请求 修改任务名称
$.ajax({
url: '/todo/modifyTask',
type: 'post',
data: JSON.stringify({_id: id, title: newTaskName}),
contentType: 'application/json',
success: function (response) {
// 将当期任务的最新状态同步到任务数组中
var task = taskAry.find(item => item._id == id);
// 修改任务名称
task.title = response.title;
// 将任务数组中的任务同步到页面中
render();
// 计算未完成任务数量
calcCount ()
}
})
});
// 用于计算未完成任务的数量
function calcCount () {
// 存储结果的变量
var count = 0;
// 将未完成的任务过滤到一个新的数组中
var newAry = taskAry.filter(item => item.completed == false);
// 将新数组的长度赋值给count
count = newAry.length;
// 将未完成的任务数量显示在页面中
strong.text(count)
}
</script>
服务器端:
// 引入express框架
const express = require('express');
// 工具库
const _ = require('lodash');
// 对象校验
const Joi = require('joi');
// 创建todo案例路由
const todoRouter = express.Router();
// 导入todo集合构造函数
const Task = require('../model/task');
// 获取任务列表
todoRouter.get('/task', async (req, res) => {
const task = await Task.find();
// 响应
res.send(task);
});
// 添加任务
todoRouter.post('/addTask', async (req, res) => {
// 接收客户端传递过来的任务名称
const { title } = req.body;
// 验证规则
const schema = {
title: Joi.string().required().min(2).max(30)
};
// 验证客户端传递过来的请求参数
const { error } = Joi.validate(req.body, schema);
// 验证失败
if (error) {
// 将错误信息响应给客户端
return res.status(400).send({message: error.details[0].message})
}
// 创建任务实例
const task = new Task({title: title, completed: false});
// 执行插入操作
await task.save();
// 响应
setTimeout(() => {
res.send(task);
}, 2000)
});
// 删除任务
todoRouter.get('/deleteTask', async (req, res) => {
// 要删除的任务id
const { _id } = req.query;
// 验证规则
const schema = {
_id: Joi.string().required().regex(/^[0-9a-fA-F]{24}$/)
}
// 验证客户端传递过来的请求参数
const { error } = Joi.validate(req.query, schema);
// 验证失败
if (error) {
// 将错误信息响应给客户端
return res.status(400).send({message: error.details[0].message})
}
// 删除任务
const task = await Task.findOneAndDelete({_id: _id});
// 响应
res.send(task);
});
// 清除已完成任务
todoRouter.get('/clearTask', async (req, res) => {
// 执行清空操作
const result = await Task.deleteMany({completed: true});
// 返回清空数据
res.send(result);
});
// 修改任务
todoRouter.post('/modifyTask', async (req, res) => {
// 执行修改操作
const task = await Task.findOneAndUpdate({_id: req.body._id}, _.pick(req.body, ['title', 'completed']),{new: true})
// 响应
res.send(task);
});
// 查询未完成任务数量
todoRouter.get('/unCompletedTaskCount', async (req, res) => {
// 执行查询操作
const result = await Task.countDocuments({completed: false});
// 响应
res.send({num: result})
});
// 更改任务全部状态
todoRouter.get('/changeAllTasksComplete', async (req, res) => {
// 状态
const { status } = req.query;
// 执行更改状态操作
const result = await Task.updateMany({}, {completed: status});
// 响应
res.send(result);
});
// 将todo案例路由作为模块成员进行导出
module.exports = todoRouter;
全局事件
// 当页面中有ajax请求发送时触发
$(document).on('ajaxStart', function () {
NProgress.start()
})
// 当页面中有ajax请求完成时触发
$(document).on('ajaxComplete', function () {
NProgress.done()
})
运用nprogress
进度条插件
RESTful风格
传统请求地址:
RESTful API 概述
一套关于设计请求的规范。
- GET: 获取数据
- POST: 添加数据
- PUT: 更新数据
- DELETE: 删除数据
RESTful API 的实现:
客户端:
// 获取用户列表信息
$.ajax({
type: 'get',
url: '/users',
success: function (response) {
console.log(response)
}
})
// 获取id为1的用户信息
$.ajax({
type: 'get',
url: '/users/1',
success: function (response) {
console.log(response)
}
})
// 获取id为1的用户信息
$.ajax({
type: 'delete',
url: '/users/10',
success: function (response) {
console.log(response)
}
})
// 获取id为1的用户信息
$.ajax({
type: 'put',
url: '/users/10',
success: function (response) {
console.log(response)
}
})
服务器端:
// 获取用户列表信息
app.get('/users', (req, res) => {
res.send('当前是获取用户列表信息的路由');
});
// 获取某一个用户具体信息的路由
app.get('/users/:id', (req, res) => {
// 获取客户端传递过来的用户id
const id = req.params.id;
res.send(`当前我们是在获取id为${id}用户信息`);
});
// 删除某一个用户
app.delete('/users/:id', (req, res) => {
// 获取客户端传递过来的用户id
const id = req.params.id;
res.send(`当前我们是在删除id为${id}用户信息`);
});
// 修改某一个用户的信息
app.put('/users/:id', (req, res) => {
// 获取客户端传递过来的用户id
const id = req.params.id;
res.send(`当前我们是在修改id为${id}用户信息`);
});
XML
XML 的全称是 extensible markup language,代表可扩展标记语言,它的作用是传输和存储数据。
客户端:
var btn = document.getElementById('btn');
var container = document.getElementById('container');
btn.onclick = function () {
var xhr = new XMLHttpRequest();
xhr.open('get', '/xml');
xhr.send();
xhr.onload = function () {
// xhr.responseXML 获取服务器端返回的xml数据
var xmlDocument = xhr.responseXML;
var title = xmlDocument.getElementsByTagName('title')[0].innerHTML;
container.innerHTML = title;
}
}
服务器端:
app.get('/xml', (req, res) => {
res.header('content-type', 'text/xml');
res.send('<message><title>消息标题</title><content>消息内容</content></message>')
});