【Ajax编程】Ajax的基础知识
Ajax编程
一、传统网站中存在的问题
- 网速慢的情况下,页面加时长,用户只能等待
- 表单提交后,如果一项内容不合格,需要重新填写所有表单内容
- 页面跳转,重新加载页面,造成资源浪费,增加用户等待时间
二、Ajax概述
01.基本概念
- 它是浏览器提供的一套方法,可以实现页面无刷新更新数据,提高用户浏览网站应用的体验
- 局部更新页面的数据,而不是整体刷新
- 在不刷新页面的情况下,向服务器端提交请求
02.应用场景
- 页面上拉加载更多数据
- 列表数据无刷新分页
- 表单项离开焦点数据验证
- 搜索框提示文字下拉列表
03.运行环境
- 需要运行在网站环境中才能生效,不能直接打开本地页面
04.运行原理
Ajax
相当于浏览器发送请求与接收响应的代理人,以实现:在不影响用户浏览页面的情况下,局部更新页面数据,从而提高用户体验
05.Ajax实现步骤
(1)创建Ajax对象
-
使用内置对象
var xhr = new XMLHttpRequest();
(2)告诉Ajax请求地址以及请求方式
xhr.open("请求方式","请求地址");
(3)发送请求
xhr.send();
(4)获取服务器端给客户端的相应数据
xhr.onload= function() {
console.log(xhr.responseText);
}
xhr.responseText()
即为服务器端响应的数据
06.服务器端响应的数据格式
-
服务器端响应数据的格式:
JSON
对象 -
但在http请求与响应的过程中,无论是请求参数还是响应内容都是以字符串类型进行传输的
- 因此,如果传输的是对象格式,可以通过
JSON.parse()
方法,将JSON字符串转换成JSON对象
xhr.onload = ()=>{ let responseText = JSON.parse(xhr.responseText); console.log(responseText); }
- 因此,如果传输的是对象格式,可以通过
07.请求参数的传递
(1)GET请求参数的传递(地址栏)
-
需要拼接请求参数
var params = "name=" + name + "&age=" + age ;
-
在地址栏拼接请求参数
xhr.open("get","请求地址?" + params);
(2)POST请求参数的传递(请求体)
-
必须设置请求报文(请求参数格式的类型)
报文主要存储请求参数
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
-
仍然需要拼接请求参数
var params = "name=" + name + "&age=" + age ;
-
设置请求方式和请求地址
xhr.open("post","请求地址");
-
然后发送请求
xhr.send(params);
(3)请求参数的格式
-
第一种:
application/x-www-form-urlencoded
name=Ruovan&age=24
-
第二种:
application/json
{name:"Ruovan",age:"24"}
-
在请求头中指定
Content-Type
属性的值是application/json
,告诉服务器端当前请求参数的格式是json
-
还需要将JSON对象转换为JSON字符串,放在 send()方法中,因为传递过程中,是以字符串形式传送的
JSON.stringify(JSON对象);
-
-
【注意】:get请求是不能提交
json
对象数据格式的,传统网站的表单提交也是不支持json
对象数据格式的
08.获取服务端响应的另一种方式
-
onreadystatechange
-
在创建ajax对象,配置ajax对象,发送请求,以及接收完服务器端响应数据,这个过程中的每个步骤都会对应一个数,这个数值就是ajax状态码
- 0:请求未初始化,没有调用
open()
- 1:请求已经建立,但是还没有发送,没有调用
send()
- 2:请求已经发送
- 3:请求正在处理中,通常响应中已经有部分数据可以用了
- 4:响应已经完成,可以获取并使用服务器的响应了
xhr.readyState; //获取Ajax状态码 // 当ajax状态码发生变化的时候触发 xhr.onreadystatechange = ()=>{ console.log(xhr.readyState); }
- 0:请求未初始化,没有调用
09.Ajax错误处理
-
网络畅通,服务器端能接收到请求,服务器端返回的结果不是预期结果
- 可以判断服务器端返回的状态码,分别进行处理
- 使用
xhr.status
,以获取http状态码
-
网络畅通,服务器端没有接收到请求,返回404状态码
- 检查请求地址是否错误
-
网络畅通,服务器端能接收到请求,服务器端返回500状态码
- 服务器端错误
-
网络中断,请求无法发送到务器端
-
会触发
xhr
对象下面的onerror
事件,在onerror
事件处理函数中对错误进行处理xhr.onerror = ()=>{ console.log("网络中断"); };
-
10.低版本浏览器的缓存问题
-
问题:在低版本的浏览器中,Ajax请求有严重的缓存问题
- 即在请求地址不发生变化的情兄下,只有第一次请求会真正发送到服务器端
- 后续的请求都会从浏览器的缓存中获取结果
- 即使服务器端的数据更新了,客户端依然拿到的是缓存中的旧数据
-
解决方案:在请求地址的后面添加请求参数,保证每次请求中的请求参数的值不相同,这样就不会从浏览器的缓存中获取数据
xhr.open("get","请求地址?t=" = Math.random());
11.同步异步概述
- AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)
- Ajax请求属于异步代码
12.Ajax封装
-
问题:发送一次请求代码过多,发送多次请求代码冗余且重复
-
解决方案:将请求代码封装到函数中,发请求时调用函数即可
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); // 将下面的所有options对象都使用defults对象代替,因为options对象里面的属性已经拷贝给了defaults对象 // 创建ajax对象 var xhr = new XMLHttpRequest(); // 拼接请求参数的对象 var params = ""; for(var attr in defaults.data){ // 将参数转换为字符串格式 params += attr + "=" + defaults.data[attr] + "&"; } // 截取字符串,取消最后一个 & 符号 params = params.substr(0, params.length-1); // 判断请求方式 if(defaults.type == "get" ){ // 如果是GET请求,就将参数拼接在请求地址中 defaults.url = defaults.url + "?" + params; } // 配置ajax对象 xhr.open(defaults.type, defaults.url); // 发送请求 if(defaults.type == "post"){ // 用户希望向服务器端传递的请求参数的类型 var contentType = defaults.header["Content-Type"]; // post请求必须设置请求报文 xhr.setRequestHeader("Content-Type",contentType); // post请求要send参数 // 判断类型 if(contentType == "application/json"){ // 如果是JSON格式,需要将传进来的JSON对象,转换为JSON字符串 xhr.send(JSON.stringify(defaults.data)); }else{ // 传递拼接的字符串参数 xhr.send(params); } }else{ xhr.send(); } // 监听onload事件 xhr.onload = function(){ // xhr.getResponseHeader(),获取响应头中的数据 var contentType = xhr.getResponseHeader("Content-Type"); // 获取服务器端返回的数据 var responseText = xhr.responseText; // 如果响应类型中包含application/json,即为JSOn数据格式 if(contentType.include("application/json")){ // 将从服务器端响应得到的数据,从JSON字符串格式转换为JSON对象格式 responseText = JSON.parse(responseText); } // 判断请求是否成功 if( xhr.status == 200){ defaults.success(responseText, xhr); }else{ defaults.error(responseText, xhr); } } } // 调用ajax函数发送ajax请求 ajax({ // 请求方式 type: "get", // 请求地址 url: "URL", // 请求参数 data: { name: "Ruovan", age: 24 }, // 请求报文类型 header: { "Content-Type": "application/x-www-form-urlencoded" }, // 请求成功 success: function(data) { console.log(data); }, // 请求失败 error: function(data, xhr){ console.log(data); console.log(xhr); } });
-
注意事项:
- 请求参数的位置问题:判断是什么请求方式以处理参数的传递
- 请求参数的格式问题:传递对象数据类型
- 请求参数的类型问题:将参数类型作为对象的一个属性,单独传入
- 默认配置:在函数体内设置一个对象存储默认值,在调用函数时,采用assign浅拷贝的方式赋值给默认对象
三、模板引擎
01.下载使用
-
引入
template-web
JS文件 -
准备模板
<script id="tpl" type="text/html"> </script>
- 必须定义id属性,作为模板的唯一标识,因为同一个页面可能会有多个模板
- 因为需要在
script
标签内部写html语法,需要将type属性设置为text/html
,可以正常解析、高亮显示
-
拼接数据
var html = template("tpl", {...});
template
传入两个参数:模板id,需要拼接的数据
-
将拼接好的html字符串添加到页面
document.querySelector("target").innerHTML = html
-
通过模板语法告诉模板引擎,数据和HTML字符串如何拼接(使用方法同
nodejs
的模板语法)<script id="tpl" type="text/html"> <section> {{ data }} </section> </script>
四、FormData
对象
01.作用
- 模拟HTML表单,相当于将HTML表单映射成表单对象,自动将表单对象中的数据拼接成请求参数的格式
- 异步上传 二进制文件
02.使用
-
准备HTML表单
-
使用
FormData
构造函数,将HTML表单转化为formData
对象var form = document.querySelector("#form"); var formData = new FormData(form);
-
提交表单对象
xhr.send(formData);
-
服务器端处理数据:
-
不能使用
body-parser
模块处理formData
对象的数据 -
需要使用
formidable
模块// 创建formidable表单解析对象 const form = new formidable.IncomingForm(); // 解析客户端传递过来的FormData对象 form.parse(req, (err, fileds, files) => { res.send(fileds); });
-
03.方法
-
获取
formData.get("属性名");
-
设置
formData.set("属性名", "属性值");
- 属性存在则更改
- 属性不存在则创建
-
删除
formData.delete("属性名");
-
追加
formData.append("属性名", "属性值");
- 与设置
set
的区别set
会覆盖已有值append
会保留原有属性及其属性值,并新增一个相同属性
- 与设置
五、二进制文件
01.上传
-
获取文件元素
var file = document.querySelector("#file"); // 注意,获取 file 元素,是一个集合
-
触发文件选择事件
file.onchange = function(){ }
-
创建空表单对象
file.onchange = function(){ var formData = new FormData(); }
-
将用户选择的文件追加到
formData
表单对象中formData.append("fileName",this.file[0]); // 这个 fileName 是自定义的属性名
-
Ajax
处理var xhr = new XMLHttpRequest(); // 请求方式必须是POST xhr.open("post","URL"); xhr.send(formData); xhr.onload();
-
服务器端处理
// 创建formidable表单解析对象 const form = new formidable.IncomeForm(); // 设置客户端上传文件的存储路径 form.uploadDir = path.join(__dirname, "public", "uploads"); // 保留上传文件的后缀名,默认是没有后缀的 form.keepExtensions = true; // 解析FormDatad对象 form.parse(req, (err, fileds, files) => { res.send(fileds); });
02.上传进度
-
持续触发
xhr.upload.onprogress = function(ev){ // 计算进度条宽度, // ev.loaded是文件已上传多少,ev.total是上传文件的总大小 let result = (ev.loaded / ev.total) * 100 + "%"; // 设置进度条宽度及内容 }
03.图片即时预览
-
获取文件路径
通过上传文件时设置的文件属性名
fileName
这个属性名存储在
files
中// 解析FormDatad对象 form.parse(req, (err, fileds, files) => { let filePath = files.fileName.path; //该路径是绝对路径,需要分割 filePath = filePath.split("public")[1]; // "/public/uploads/..." // 通过对象形式返回 res.send({ path: filePath }); });
-
客户端处理
xhr.onload = function(){ if(xhr.status ==200 ){ // 获取文件路径,对象化 var result = JSON.parse(xhr.responseText); // 动态创建img表单 var img = document.createElement("img"); img.src = result.path; // 获取图片容器 var box = documnet.querySelector("#box"); // 加载完成后显示 img.onload = function(){ box.appendChild(img); } } }
六、Ajax请求限制
01.限制
- Ajax只能向自己的服务器发送请求
- 浏览器的同源政策的限制:无法向非同源地址发送Ajax请求,如果请求,浏览器就会报错
02.同源政策
-
如果两个页面拥有相同的协议、域名和端口,那么这两个页面就属于同一个源,其中只要有一个不相同,就是不同源
-
同源政策的目的
- 同源政策是为了保证用户信息的安全,防止恶意的网站窃取数据
- 最初的同源政策是指A网站在客户端设置的Cookie,B网站是不能访问的
03.解决办法
-
使用 JSONP解决同源限制问题
- JSON with Padding
- 它不属于Ajax请求,但它可以模拟Ajax请求
- JSON 是一种数据格式, JSONP是实现跨域请求的一种方式
-
将不同源的服务器端请求地址写在 script标签的
src
属性中src
属性不受同源政策的影响,可以向非同源端发送请求 -
服务器端响应数据必须是一个函数的调用,真正要发送给客户端的数据需要作为函数调用的参数
-
在客户端全局作用于下定义函数
func
,且需要放在script
标签前面 -
在函数内部对服务器端返回的数据进行处理
-
代码示例
-
客户端
<!-- 1.定义一个全局函数func,--> <script> function func(data){ } </script> <!-- 2.引入另一个源的服务器请求地址,(当前源的端口是3000) --> <!-- 当这个script标签加载完成,就会向另一个源发送请求,然后接受服务器的返回结果,调用函数--> <script src="http://localhost:3001/test"></script>
-
服务器端
app.get("/test",(req,res)=>{ // 这里不能调用func,因为未定义,需要将 函数调用 写成字符串形式 const fn = "func()"; res.send(fn); })
-
04.代码优化
-
script
请求变为动态请求- 点击按钮,动态创建
script
标签,并设置src
属性 - 监听
script
加载完成事件onload
,事件加载完成后,就删除script
标签
- 点击按钮,动态创建
-
函数名称问题:客户端和服务端需要一致
-
将客户端的函数名作为参数传递给服务器端:
// http://localhost:3001/test?callback=func app.get("test",(req,res)=>{ const fnName = req.query.callback; // 转换数据为对象字符串 const data = JSON.stringify({...}); // 传递数据给函数调用 const fn = fnName + "(" + data + ")"; res.send(fn); })
app.get("test",(req,res)=>{ // express框架下,res提供了一个jsonp方法,以实现上面的功能 res.jsonp({...}); })
-
-
封装JSONP函数
function jsonp(options){ // 设置随机函数名,生成随机数,转换字符串,替换小数点 let funName = "myJsonp" + Math.random().toSting().replace(".","") // 设置为全局函数,注意:不能window.fnName,否则会创建新的属性,变量只能用[] window[funName] = options.success; // 设置拼接数据中的数据为字符串 let params = ""; for(let attr in options.data){ params += "&" + attr + "=" + options.data[attr]; } // 动态创建script标签 let script = document.createElement("script"); // 设置src属性,并添加函数参数和请求数据 script.src = options.url + "?callback=" + funName + params; // 加入页面 let body = document.querySelector("#body"); body.append(script); // 删除script标签 script.onload = function(){ body.removeChild(script); } } jsonp({ // 传入请求地址 url: "http://localhost:3000/test", data: { }, success: function(){ // 请求成功函数 } })
05.CORS跨域资源共享
-
CORS:全称为
Cross-origin resource sharing
,即跨域资源共享(即非同源)-
它允许浏览器向跨域服务器发送Ajax请求,克服了Ajax只能同源使用的限制
-
客户端请求:
origin: http://localhost:3000
-
服务器端响应:
Access-Control-Allow-origin: "http://localhost:3000" Access-Control-Allow-Origin: "*"
*
代表允许所有客户端访问
-
-
语法示例
app.use((req,res,next)=>{ // *号,代表允许所有客户端的访问, res.header("Access-Control-Allow-Origin","*"); // 设置客户端的请求方式 res.header("Access-Control-Allow-Methods","get,post"); next(); });
06.服务器端解决方案
-
同源政策是浏览器给予Ajax技术的限制,服务器端不存在同源政策限制
-
首先让1号客户端向1号服务器端发送请求
-
然后由1号服务器端访问2号服务器端的数据
-
最后让1号服务器端获取的数据响应给1号客户端
-
需要引入第三方模块
request
// 引入模块 const request = require("request"); // 返回一个方法
app.get("/server",(req,res)=>{ request("2号服务器端地址",(err, response, body) => { res.send(body); }); });
由1号客户端向1号服务器端发送请求,请求地址是/server
然后在1号服务器端中调用request()方法,向2号服务器端发送请求,以获取数据
2号服务器端作出响应给1号服务器端,再由1号服务器响应给1号客户端
-
07.cookies
-
实现客户端与服务器端的身份识别
-
但在跨域时,cookies不会随请求发送
-
withCredentials
:在响应头设置指定在涉及到跨域请求时,是否携带 cookie 信息,默认值为 false -
Access-Control-Allow-Credentials:true
:允许客户端发送请求时携带 cookie
-
-
语法示例:
xhr.withCredentials = true;
res.header("`Access-Control-Allow-Credentials","true");
七、$.ajax()
方法
-
调用
$.ajax()
方法,需要传递一个对象参数$.ajax({ type: "get", url: "URL", // 自动转换为字符串形式:name=Ruovan&age=24 data: { name: "Ruovan", age: 24 }, // application/json contentType: "application/x-www-form-urlencoded", beforeSend: function(){ return false; }, // 根据服务器端在响应头设置的数据的类型,自动转换成对应数据类型 success: function(response){ }, error: function(xhr){ } });
-
$.ajax()
也可以发送jsonp
请求$.ajax({ url: "/jsonp", // 代表现在要发送的是jsonp请求 dataType: "jsonp", // 修改callback参数名称,即请求地址后面的?callback=fn,更改为?cb=fn jsonp: "cb", // 指定函数名称 jsonpCallback: "fnName", success: function(response){ } });
app.get("/jsonp",(req,res) => { const cb = req.query.cb; const data = cd + "()"; res.send(data); })
-
-
serialize
方法-
将表单中的数据自动拼接成字符串类型的参数
var params = $("#form").serialize();
-
封装函数:将表单中用户输入的内容转换为对象类型
function serializeObject(obj){ // obj是表单jQuery对象 // serializeArray()方法将页面中表单元素的内容转换为一个数组集合 // 例:[{name:"username",value:"Ruovan"},{name:"age",value:"24"}] var params = obj.serializeArray(); // 设置变量存储数据 var result = {}; // 遍历集合,取出数据,存储为对象类型 $.each(params, function(index, value){ // index:索引号; // value:对象 {name:"username",value:"Ruovan"} // 需要将属性名和属性值取出来,存储在result中 result[value.name] = value.value; }); return result; }
-
-
$.get()
、$.post()
方法- 用于发送GET、POST请求
$.get("url",{...},function(response){ }); // 第二个参数是可选参数,用于传递数据
-
Ajax全局事件
- 只要页面中有Ajax请求被发送,对应的全局事件就会被触发
.ajaxStrat()
、.ajaxComplete()
// 当页面中有ajax请求发送时触发 $(document).on("ajaxStart",function(){ NProgress.start(); }); // 当页面中有ajax请求完成时触发(无论请求成功还是失败) $(document).on("ajaxComplete",function(){ NProgress.done(); });
-
NProgress
插件:显示进度条-
引入
css
、js
文件<link rel="stylesheet" href="nprogress.css"> <script src="nprogress.js"></script>
-
调用方法:
NProgress.start(); //进度条开始运动 NProgress.done(); //进度条结束运动
-
八、RESTful API
01.一套关于设计请求的规范
GET:获取数据
POST:添加数据
PUT:更新数据
DELETE:删除数据
九、XML基础(略)
01.基本概念
- XML的全称是
extensible markup language
,代表可扩展标记语言,它的作用是传输和存储数据
02.XML DOM
- XML DOM即XML文档对象模型,是w3c组织定义的套操作XML文档对象的API,浏览器会将ⅩML文档解析成文档对象模型
app.get("/xml",(req,res)=>{
res.header("content-type","text/xml");
res.send();
});
<script>
xhr.onload = function(){
let xmlDocument = xhr.responseXML;
// 与HTML的DOM操作类似
}
</script>