前后端交互(一)——Ajax的使用
课堂目标
- 理解ajax基本使用
- 会使用XMLHttpRequest对象实现数据交互
- 了解onreadystatechange服务器响应信息
- 会使用FormData对象上传文件
- 了解upload事件对象
知识要点
- ajax使用
- XMLHttpRequest对象
- FormData对象
- upload 事件对象
登录简单回顾
1、提出ajax验证用户名需求;
2、如果通过跳转解决很麻烦;
一、利用ajax来解决验证用户名问题
-
ajax是: Ajax 即“Asynchronous Javascript And XML”(异步 JavaScript 和 XML)
-
ajax的基本使用;
-
新建XMLHttpRequest对象;
let xhr = new XMLHttpRequest(); -
配置请求参数
xhr.open("get",`/checkUserName?username=${this.value}`,true); //true是异步,false是同步 -
接收返还值
xhr.onload = function(){ let res = JSON.parse(xhr.responseText); // xhr.responseText是后端给的返还值 } // 案例代码 xhr.onload = function () { // console.log(xhr.responseText); // 将json格式转换为数组 let res = JSON.parse(xhr.responseText) console.log(res); // 调整内容 document.querySelector('.exchange').innerHTML = res.info; // 调整颜色 if(res.status==1){ document.querySelector('.exchange').style.color="green" }else{ document.querySelector('.exchange').style.color="red" } } -
发送服务器
xhr.send();
-
-
ajax后端实现
router.get("/checkUserName", (ctx, next) => { // console.log(ctx.query); // ctx.body=ctx.query.username // 寻找数据里有没有输入的用户名 let res = UsersData.find(v => v.username == ctx.query.username) if (res) { ctx.body = { status: 1, info: "用户名正确" } } else { ctx.body = { status: 2, info: "用户名错误" } } })
二、针对ajax的详细解释
get注意点
-
get通过parmas传参,ctx.parmas
-
前端代码
document.querySelector("button").onclick = function () { let xhr = new XMLHttpRequest(); xhr.open("get", "/get/3", true); xhr.onload = function () { console.log(xhr.responseText); }; xhr.send(); } -
后端代码
router.get('/get/:id', (ctx, next) => { console.log(ctx.params); ctx.body = { status: 1, info: "请求成功" } })
-
-
get和querystring的问题,通过url传参,问号带参,?username=${this.value} (见上面例子)
post注意点
-
前端代码
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"); let data = "username=王五&&age=18"; // 或者数据为json模式 xhr.setRequestHeader("Content-type","application/json"); //json编码 let data = JSON.stringify({ username:"王五", age:20 }) xhr.send(data); } -
后端代码
// 需要用到koa-body,解决post传参问题 const koaBody = require("koa-body") app.use(koaBody()) router.post("/post", (ctx, next) => { console.log(ctx.request.body); ctx.body={ static:1, info:'请求成功' } })
// 发送数据时候需要设置http正文头格式;不同编码需传递不同形式数据
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); //默认编码
xhr.setRequestHeader("Content-type","multipart/form-data"); //二进制编码
xhr.setRequestHeader("Content-type","application/json"); //json编码
-
获取头部信息;(xhr.onload里获取)
- getAllResponseHeaders 或者是getResponseHeader ;
-
koa-body 解决post传参问题
三、同步及异步ajax
- 设置true和false区别:true 是异步,false 是同步,异步不影响其他程序执行,同步得先请求完再执行其他程序
四、onreadystatechange
类似于onload
onreadystatechange:存有处理服务器响应的函数,每当 readyState 改变时,onreadystatechange 函数就会被执行。
readyState:存有服务器响应的状态信息。
- 0: 请求未初始化(代理被创建,但尚未调用 open() 方法)
- 1: 服务器连接已建立(
open方法已经被调用) - 2: 请求已接收(
send方法已经被调用,并且头部和状态已经可获得) - 3: 请求处理中(下载中,
responseText属性已经包含部分数据) - 4: 请求已完成,且响应已就绪(下载操作已完成)
status常用状态码
| HTTP状态码 | 描述 |
| 100 | 继续。继续响应剩余部分,进行提交请求 |
| 200 | 成功 |
| 301 | 永久移动。请求资源永久移动到新位置 |
| 302 | 临时移动。请求资源零时移动到新位置 |
| 304 | 未修改。请求资源对比上次未被修改,响应中不包含资源内容 |
| 401 | 未授权,需要身份验证 |
| 403 | 禁止。请求被拒绝 |
| 404 | 未找到,服务器未找到需要资源 |
| 500 | 服务器内部错误。服务器遇到错误,无法完成请求 |
| 503 | 服务器不可用。临时服务过载,无法处理请求 |
document.querySelector("button").onclick = function () {
let xhr = new XMLHttpRequest();
xhr.open("get", "/get/3", true);
// xhr.onload = function () {
// console.log(xhr.responseText);
// };
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
console.log(xhr.responseText);
}
}
}
xhr.send();
}
五、返还数据类型
服务器返还json数据
xhr.responseText //来获取
服务器返还xml数据
xhr.responseXML //获取值
-
前端代码
<script> document.querySelector("button").onclick = function () { let xhr = new XMLHttpRequest(); xhr.open("get", "/xml", true); xhr.onload = function () { // console.log(xhr.response); // console.log(xhr.responseText); console.log(xhr.responseXML); //返还xml结构 // 获取dom节点内容, let name = xhr.responseXML.getElementsByTagName("name")[0].innerHTML console.log(name); //这里返还nodejs实战 }; xhr.send(); } </script> -
后端代码
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>50元</price> </nodejs> <react> <name>react入门</name> <price>60元</price> </react> </books>` }) -
重写response里的content-type内容
-
xhr.overrideMimeType('text/xml; charset = utf-8')
六、利用FormData来实现文件上传
简易版的文件上传
-
前端代码
<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); // 定义一个FormData对象 let form = new FormData(); form.append('img', file); let xhr = new XMLHttpRequest(); // 上传文件必须得是post xhr.open('post', '/upload', true) xhr.onload = function () { console.log(xhr.responseText); }; xhr.send(form); } </script> -
后端代码
const koaBody = require("koa-body") const fs = require("fs") //引入fs模块 app.use(koaBody({ multipart: true //这个属性必须设置,才能接收文件 })) router.post("/upload", (ctx, next) => { // console.log(ctx.request.files.img); // 找到文件上传的路径 let fileData = fs.readFileSync(ctx.request.files.img.path) //这个img和前端设置的img保持命名一致 // 写一个文件保存的路径 fs.writeFileSync("static/imgs/" + ctx.request.files.img.name, fileData) ctx.body = "请求成功" })
添加进度和速度
-
创建FormData对象
-
监控上传进度
upload 事件钩子
- onloadstart 上传开始
- onprogress 数据传输进行中
- evt.total :需要传输的总大小;
- evt.loaded :当前上传的文件大小;
- onabort 上传操作终止
- onerror 上传失败
- onload 上传成功
- onloadend 上传完成(不论成功与否)
-
取消上传调用xhr.abort()方法
前端代码
<body>
<input type="file" class="myfile" />
进度:<progress value="0" max="100 "></progress><span class="percent"></span>
速度:<span class="speed">20b/s</span>
<button>上传文件</button>
<button>取消上传</button>
</body>
<script>
let xhr = new XMLHttpRequest();
let btns = document.querySelectorAll("button")
// 设置上传开始的时间和传的大小
let startTime
let startLoaded
// 点击上传
btns[0].onclick = function () {
let file = document.querySelector(".myfile").files[0];
let form = new FormData();
form.append('myfile', file);
xhr.open("post", "/fileUpload", true);
xhr.onload = function () {
console.log(xhr.responseText);
};
// 以下是upload 事件钩子
xhr.upload.onloadstart = function () {
console.log("上传开始");
startTime = new Date().getTime();
startLoaded = 0;
}
xhr.upload.onprogress = function (evt) {
console.log("上传当中");
// 以下处理速度
let endTime = new Date().getTime(); // 一阶段结束时间
let dTime = (endTime - startTime) / 1000; // 时间差,毫秒/1000
let dLoaded = evt.loaded - startLoaded; // 一阶段上传大小
let speed = dLoaded /dTime ; // 速度
startTime = new Date().getTime();
startLoaded = evt.loaded;
let unit = "b/s"; // 单位
if (speed / 1024 > 1) {
unit = "kb/s"
speed = speed / 1024
}
if (speed / 1024 > 1) {
unit = "mb/s"
speed =speed / 1024
}
document.querySelector(".speed").innerHTML = speed.toFixed(2) + unit
// 以下处理进度
// 百分比
let percent = (evt.loaded / evt.total * 100).toFixed(0)
// 进度条
document.querySelector("progress").value = percent;
// 进度数值
document.querySelector(".percent").innerHTML = percent + "%"
}
xhr.upload.onload = function () {
console.log("上传成功");
}
xhr.upload.onloadend = function () {
console.log("上传完成");
}
xhr.upload.onabort = function () {
console.log("上传取消");
}
xhr.send(form)
}
// 点击取消上传
btns[1].onclick = function () {
xhr.abort();
document.querySelector("progress").value = 0;
document.querySelector(".percent").innerHTML = 0 + "%"
}
</script>
后端代码
const koaBody = require("koa-body")
const fs = require("fs") //引入fs模块
app.use(koaBody({
multipart: true //这个属性必须设置,才能接收文件
}))
router.post("/upload", (ctx, next) => {
// console.log(ctx.request.files.img);
// 找到文件上传的路径
let fileData = fs.readFileSync(ctx.request.files.myfile.path) //这个img和前端设置的myfile保持命名一致
// 写一个文件保存的路径
fs.writeFileSync("static/imgs/" + ctx.request.files.myfile.name, fileData)
ctx.body = "请求成功"
})