前后端交互01
课堂目标
- 理解ajax基本使用
- 会使用XMLHttpRequest对象实现数据交互
- 了解onreadystatechange服务器响应信息
- 会使用FormData对象上传文件
- 了解upload事件对象
知识要点
- ajax使用
- XMLHttpRequest对象
- FormData对象
- upload 事件对象
登录简单回顾
1、提出ajax验证用户名需求;
2、如果通过跳转解决很麻烦;
利用ajax来解决验证用户名问题
-
ajax的前端实现
-
ajax后端实现
-
ajax是: Ajax 即“Asynchronous Javascript And XML”(异步 JavaScript 和 XML)
XML: 和JSON一样是目前主流的两种返还格式ajax的特点: 无刷新,局部更新;
-
ajax的基本使用;
- 新建XMLHttpRequest对象
let xhr = new XMLHttpRequest();
- 配置请求参数
xhr.open("get","/checkUser",true); //true是异步,false是同步
- 接收返还值
xhr.onload = function(){ let res = JSON.parse(xhr.responseText); }
- 发送服务器
xhr.send();
针对ajax的详细解释
get/post在ajax中的使用
- ajax中的get请求
- get注意点:
- get和querystring的问题,通过url传参(?带参方式)
xhr.open("get", `/checkUserName?username=${this.value}` ,true)
- get通过parmas传参(跟querystring区别是不带参数会错误)必须给参数,否则地址错误。(? 之前)
xhr.open("get",'/get/4',true) // 带个4的参数,在后台路由指定是什么,id // router.get("/get/:id",(ctx,next)=>{})
- query方式传参,则不会影响地址。(?之后)
- post也可以使用query方式,不是get独有的;
get.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<button>点击发送ajax</button>
</body>
<script>
document.querySelector("button").onclick = function(){
let xhr = new XMLHttpRequest();
xhr.open("get","/get/4",true);
xhr.onload = function(){
console.log(xhr.responseText);
}
xhr.send();
}
</script>
</html>
index.js(koa)
router.get("/get/:id",(ctx,next)=>{
console.log(ctx.params);
ctx.body = {
status:1,
info:"请求成功"
}
})
-
post注意点:
- post传参是没有数据量限制的,通常会是服务器限制;
- post传参是正文区域传参所以需要设置编码格式,而get是在头部url中,所以不需要;
- 发送数据时候需要设置http正文头格式:
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); //默认编码 xhr.setRequestHeader("Content-type","multipart/form-data"); //二进制编码 xhr.setRequestHeader("Content-type","application/json"); //json编码
- 获取头部信息;
getAllResponseHeaders
或者是getResponseHeader
;
-
post.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<!-- <form action="" enctype="application/x-www-form-urlencoded"></form> -->
<button>点击我发送ajax</button>
</body>
<script>
document.querySelector("button").onclick = function () {
let xhr = new XMLHttpRequest();
xhr.open("post", "/post", true);
xhr.onload = function () {
console.log(xhr.responseText);
// 获取返还头信息
// console.log(xhr.getAllResponseHeaders());
// console.log(xhr.getResponseHeader("content-type"));
}
// xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
// xhr.setRequestHeader("Content-Type","multipart/form-data"); //二进制编码
xhr.setRequestHeader("content-type","application/json");
// let data = "username=王五&age=20";
let data = JSON.stringify({
username:"王五",
age:20
})
xhr.send(data);
}
</script>
</html>
- index.js(koa)
const koaBody = require("koa-body");
app.use(koaBody({
multipart:true
}));
router.post("/post",(ctx,next)=>{
console.log(ctx.request.body);
ctx.body = {
status:1,
info:"post请求成功"
}
})
onreadystatechange
onreadystatechange:存有处理服务器响应的函数,每当 readyState 改变时,onreadystatechange 函数就会被执行。
readyState:存有服务器响应的状态信息(老的方式,请求状态,可以不用)
-
0: 请求未初始化(代理被创建,但尚未调用 open() 方法)
-
1: 服务器连接已建立(
open
方法已经被调用) -
2: 请求已接收(
send
方法已经被调用,并且头部和状态已经可获得) -
3: 请求处理中(下载中,
responseText
属性已经包含部分数据) -
4: 请求已完成,且响应已就绪(下载操作已完成)
-
readyStatus.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<button>点击</button>
</body>
<script>
document.querySelector("button").onclick = function () {
let xhr = new XMLHttpRequest();
xhr.open("get", "/get/4", true);
xhr.onload = function () {
console.log(xhr.responseText);
console.log(xhr.readyState);
console.log(xhr.status);
}
// xhr.onreadystatechange = function () {
// if (xhr.readyState == 4) {
// if (xhr.status == 200) {
// console.log(xhr.responseText);
// }
// }
// }
xhr.send();
}
</script>
</html>
status常用状态码
HTTP 状态码描述
100 继续。继续响应剩余部分,进行提交请求
200 成功
301 永久移动。请求资源永久移动到新位置
302 临时移动。请求资源零时移动到新位置
304 未修改。请求资源对比上次未被修改,响应中不包含资源内容
401 未授权,需要身份验证
403 禁止。请求被拒绝
404 未找到,服务器未找到需要资源
500 服务器内部错误。服务器遇到错误,无法完成请求
503 服务器不可用。临时服务过载,无法处理请求
-
返还数据类型
-
服务器返还json数据
xhr.responseText //来获取 文本
-
服务器返还xml数据
xhr.responseXML //获取值 页面
- 重写response里的content-type内容
- xhr.overrideMimeType(‘text/xml; charset = utf-8’)
- 服务端若返回xml格式数据需设置响应头(koa中)
ctx.set(“content-type”,“text/xml”)
-
-
xml.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<button>点击我获取xml</button>
</body>
<script>
document.querySelector("button").onclick = function(){
let xhr = new XMLHttpRequest();
xhr.open("get","/xml",true);
// 重写content-type;
xhr.overrideMimeType("text/xml");
xhr.onload = function(){
// console.log(xhr.responseText);
// console.log(xhr.responseXML);
// console.log(xhr.response)
let name = xhr.responseXML.getElementsByTagName("name")[1].innerHTML;
console.log(name);
}
xhr.send();
}
</script>
</html>
- index.js(koa)
router.get("/xml",(ctx,next)=>{
// ctx.set("content-type","text/xml");
ctx.body = `<?xml version='1.0' encoding='utf-8' ?>
<books>
<nodejs>
<name>nodejs实战</name>
<price>56元</price>
</nodejs>
<react>
<name>react入门</name>
<price>50元</price>
</react>
</books>
`
})
-
同步及异步ajax;
- 设置true和false区别;
1.异步,任务不需等待不受影响
2.同步,等待其他任务完成
- 设置true和false区别;
-
async.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<button>按钮一</button>
<button>按钮二</button>
</body>
<script>
let btns = document.querySelectorAll("button");
btns[0].onclick = function () {
let xhr = new XMLHttpRequest();
xhr.open("get", "/get/4", true);
xhr.onload = function () {
console.log(xhr.responseText);
}
xhr.send();
}
btns[1].onclick = function(){
console.log("我是按钮二");
}
</script>
</html>
ajax的封装使用
封装步骤:
- 使用Object.assign()函数将默认配置和传递配置参数进行合并;
- 传递的method中,需要判断get/post,get传参需要使用queryString即使用?进行拼接url;post也要凭借成参数并通过send()进行传递;
- 封装拼接url方法(Object.keys(obj)和Object.values(obj)),map循环key=value并使用join("&")进行拼接(这种方式如果数据对象为多层数据,需要递归解决);
- onload()方法获取返回数据,xhr.responseText()
- 判断get/post方法,不同请求方式,send()方法不同。
- 需要给出默认headers配置,用户也可以自定义设置,通过for in设置头部setRequestHeader();设置头部后,需要switch判断头部,只有为JSON时,才能以封装的数据格式以post方式传递数据,如果是json格式,需要转为json再发送
- onload()时也需要判断返还数据类型,或者直接使用JSON.parse()直接数据转为对象
封装成类似下面形式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<!-- <form enctype="application/x-www-form-urlencoded"></form> -->
</body>
<script>
ajax({
url: "/test",
method: "post",
data: {
hello: "你好",
height: "178cm"
},
success(res) {
console.log(res)
}
})
function ajax(opts) {
let newOpts = Object.assign({ //方法用于对象的合并
url: "",
method: "get",
data: "",
async: true,
headers: {
"content-type": "application/x-www-form-urlencoded"
},
success() { }
}, opts);
let xhr = new XMLHttpRequest();
// get 是通过querystring方式传参数;
// post :send(data) data:name=zhangsan&age=20
// get url: /xml
// data:{name:zhangsan,age:20}--->/xml?name=zhangsan&age=20&height=178
// querystring是查询参&连接,不加引号
if (newOpts.method.toLowerCase() === "get") { //不管大写小写
xhr.open(newOpts.method, newOpts.url + "?" + o2u(newOpts.data), newOpts.async);
} else {
xhr.open(newOpts.method, newOpts.url, newOpts.async);
}
xhr.setRequestHeader("content-type", newOpts.headers['content-type']);
xhr.οnlοad = function () {
newOpts.success(JSON.parse(xhr.responseText));
}
let sendData = null
if (newOpts.method.toLowerCase() === "post") {
// name=zhangsan&age=20&height=178
sendData = o2u(newOpts.data);
}
xhr.send(sendData);
// let obj = {
// name:"张三",
// age:20
// }
// console.log(o2u(obj));
function o2u(obj) {
// ["hello","height"] --> 0,1
let keys = Object.keys(obj);
let values = Object.values(obj);
return keys.map((key, k) => {
console.log(k)
return key + "=" + values[k];
}).join("&");
}
}
</script>
</html>
利用FormData来实现文件上传
-
创建FormData对象
-
监控上传进度
upload 事件
上传文件事件钩子
- onloadstart 上传开始
- onprogress 数据传输进行中
- evt.total :需要传输的总大小;
- evt.loaded :当前上传的文件大小;
- onabort 上传操作终止
- onerror 上传失败
- onload 上传成功
- onloadend 上传完成(不论成功与否)
-
formdata.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<input type="file" class="myfile" />
<button>点击我上传文件</button>
</body>
<script>
document.querySelector("button").onclick = function(){
let file = document.querySelector(".myfile").files[0];
// console.log(files);
let form = new FormData();
form.append("img",file);
form.append("name","张三");
let xhr = new XMLHttpRequest();
xhr.open("post","/upload",true);
xhr.onload = function(){
console.log(xhr.responseText);
}
xhr.send(form);
}
</script>
</html>
- index.js(koa)
const koaBody = require("koa-body");
const fs = require("fs");
app.use(koaBody({
multipart:true
}));
router.post("/upload",(ctx,next)=>{
console.log(ctx.request.body);
console.log(ctx.request.files.img);
let fileData = fs.readFileSync(ctx.request.files.img.path);
fs.writeFileSync("static/imgs/"+ctx.request.files.img.name,fileData);
ctx.body = "请求成功";
})
qq空间批量上传图片案例
通过formData实现《qq空间批量上传图片》
需求确定:相册内容显示相册
nodejs搭建后台
分析上传元素
登录区分不同用户
创建上传对象
上传图片
获取上传后的最新图片数据
一、需求确定
1. 相册内容显示相册
点击上传(可选择多张图片),上传成功后,会显示相册图片;
上传时,有上传进度显示
二、nodejs搭建后台
使用nunjucks加载页面
三、分析上传元素
1. 登录区分不同用户
2. 创建上传对象
上传图片可以有多张图片:input中使用multiple="multiple"即可选择多张上传图片;
获取并显示待上传图片:通过原生this.files可以获取所有图片对象。根据文件对象循环创建HTML(createElement(“div”))放入对应容器中;
通过FileReader读取上传文件。let fileReader = new FileReader(file);fileReader.readAsDataURL(file);fileReader.onload()时将图片转为base64,并将base64作为临时路径(即在onload时再创建HTML);
继续添加时,继续创建HTML;
3. 上传图片
上传:监控上传进度,后台转存;此处上传是一个接一个进行上传,将需要上传时将html中对象一个一个添加到数组中,上传时将每个图片抽象成图片类(每张图片都有自己的创建HTML,监控上传进度,上传等方法);
将创建的节点进行保存,用于监控上传进度;
通过FormData进行上传
通过unload中的onprogress监控上传进度,onload()处理上传成功后的处理;
点击上传时,遍历存储的图片数组,并进行上传
使用Promise监控一个一个上传:调用时返回Promise对象,在每一个上传的函数中,使用async await处理即可
4. 获取上传后的最新图片数据
四、完整案例展示
回顾
1.ajax基本使用
2.get/post在ajax中的使用
3.ajax中成功的返还
4.返还数据格式
5.FormData对象
6.upload事件对象