1. ajax
- Ajax 即“Asynchronous Javascript And XML”(异步 JavaScript 和 XML)
- 新建XMLHttpRequest对象
1-1. ajax的基本使用
let xhr = new XMLHttpRequest();
xhr.open("get","/checkUser",true);
xhr.onload = function(){
let res = JSON.parse(xhr.responseText);
}
xhr.send();
1-2. 利用ajax来解决验证用户名
<script>
document.querySelector(".inputStyle").onblur = function () {
let xhr = new XMLHttpRequest();
xhr.open("get", `/checkUserName?username=${this.value}`, true);
xhr.onload = function(){
let obj = JSON.parse(xhr.responseText);
document.querySelector(".exchange").innerHTML = obj.info;
if(obj.status === 1){
document.querySelector(".exchange").style.color = "green";
}else{
document.querySelector(".exchange").style.color = "red";
}
}
xhr.send();
}
</script>
router.get("/checkUserName", (ctx, next) => {
let res = usersDate.find(v => v.username === ctx.query.username);
if (res) {
ctx.body = {
status: 1,
info: "用户名正确"
};
} else {
ctx.body = {
status: 2,
info: "用户名错误"
}
}
})
2. 参数详解
2-1. get的使用
2-1-1. get通过parmas传参
- 但是由于一旦请求地址改成
/get
则无法获得相应的处理,因此一般不用
// 请求
<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>
router.get("/get/:id",(ctx,next)=>{
console.log(ctx.params);
ctx.body = {
status:1,
info:"请求成功"
}
})
2-1-2. get通过url传参[querystring]
- get和querystring的问题,通过url传参
<script>
document.querySelector(".inputStyle").onblur = function () {
let xhr = new XMLHttpRequest();
xhr.open("get", `/checkUserName?username=${this.value}`, true);
xhr.onload = function(){
let obj = JSON.parse(xhr.responseText);
document.querySelector(".exchange").innerHTML = obj.info;
if(obj.status === 1){
document.querySelector(".exchange").style.color = "green";
}else{
document.querySelector(".exchange").style.color = "red";
}
}
xhr.send();
}
</script>
2-2. post的使用
- 由于post是通过http正文进行传参, 因此需要对正文设置编码格式,在发送数据时需要设置http正文头格式;
2-2-1.设置http正文头格式
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xhr.setRequestHeader("Content-type","multipart/form-data");
xhr.setRequestHeader("Content-type","application/json");
2-2-2. 获取头部信息
xhr.getAllResponseHeaders()
xhr.getResponseHeader("content-type")
2-2-3. 例子
<script>
document.querySelector("button").onclick = function () {
let xhr = new XMLHttpRequest();
xhr.open("post","/post",true);
xhr.onload = function(){
console.log(xhr.responseText);
}
xhr.setRequestHeader("Content-type","application/json");
let data = JSON.stringify({
username:"王五",
age:20
});
xhr.send(data);
}
</script>
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: "请求成功"
}
})
2-3. onreadystatechange
- 存有处理服务器响应的函数,每当 readyState 改变时,onreadystatechange 函数就会被执行
- 可替代 onload
2-3-1. readyState — 存有服务器响应的状态信息
- 0: 请求未初始化(代理被创建,但尚未调用 open() 方法)
- 1: 服务器连接已建立(
open
方法已经被调用) - 2: 请求已接收(
send
方法已经被调用,并且头部和状态已经可获得) - 3: 请求处理中(下载中,
responseText
属性已经包含部分数据) - 4: 请求已完成,且响应已就绪(下载操作已完成)
2-3-2. status常用状态码
HTTP状态码 | 描述 |
100 | 继续。继续响应剩余部分,进行提交请求 |
200 | 成功 |
301 | 永久移动。请求资源永久移动到新位置 |
302 | 临时移动。请求资源零时移动到新位置 |
304 | 未修改。请求资源对比上次未被修改,响应中不包含资源内容 |
401 | 未授权,需要身份验证 |
403 | 禁止。请求被拒绝 |
404 | 未找到,服务器未找到需要资源 |
500 | 服务器内部错误。服务器遇到错误,无法完成请求 |
503 | 服务器不可用。临时服务过载,无法处理请求 |
2-3-3. 例子
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(xhr.responseText);
}
}
}
3. ajax封装
function ajax(options) {
let opts = Object.assign({
method: "get",
url: "",
headers: {
'content-type': 'application/x-www-form-urlencoded'
},
jsonp: "cb",
data: "",
success: function () {}
}, options)
let xhr = new XMLHttpRequest();
// 配置路由
if (opts.method === "get") {
let data = o2u(opts.data);
opts.url = opts.url + "?" + data;
}
// 配置请求参数
xhr.open(opts.method, opts.url, true);
for (let key in opts.headers) {
xhr.setRequestHeader(key, opts.headers[key]);
}
// 设置传输的数据格式(post需要)
let sendData;
switch(opts.headers['content-type']){
case 'application/x-www-form-urlencoded':
sendData = o2u(opts.data);
break;
case 'application/json':
sendData = JSON.stringify(opts.data);
break;
}
// 接收返还值
xhr.onload = function(){
let resData;
if(xhr.getResponseHeader("content-type").includes(xml)){
resData = xhr.responseXML;
}else{
resData = JSON.parse(xhr.responseText);
}
opts.success(resData);
}
// 发送服务器
if(opts.method === "get"){
xhr.send();
}else{
xhr.send(sendData);
}
}
// 将数据转换为"name="张三"&age=20"的形式
function o2u(obj) {
let keys = Object.keys(obj);
let value = Object.values(obj);
return keys.map((v, k) => {
return `${v}=${value[k]}`
}).join("&");
}
4. xml数据的返还
4-1. 在后端设置content-type
- 若不设置content-type,则无法识别返回的xml的数据类型
- 请求
<script>
document.querySelector("button").onclick = function(){
let xhr = new XMLHttpRequest();
xhr.open("get","/xml",true);
xhr.onload = function(){
console.log(xhr.responseXML);
}
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>56元</price>
</nodejs>
<react>
<name>react入门</name>
<price>50元</price>
</react>
</books>
`
})
4-2. 在请求体中重写content-type
<script>
document.querySelector("button").onclick = function(){
let xhr = new XMLHttpRequest();
xhr.open("get","/xml",true);
xhr.overrideMimeType("text/xml")
xhr.onload = function(){
console.log(xhr.responseXML);
}
xhr.send();
}
</script>
router.get("/xml", (ctx, next) => {
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>
`
})
5. 利用FormData文件上传
5-1. 文件上传的简易实现
<script>
document.querySelector("button").onclick = function () {
let file = document.querySelector(".myfile").files[0];
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>
router.post("/upload", (ctx, next) => {
console.log(ctx.request.body);
console.log(ctx.request.files);
let fileData = fs.readFileSync(ctx.request.files.img.path);
fs.writeFileSync("practice/static/imgs/" + ctx.request.files.img.name, fileData);
ctx.body = "请求成功";
})
5-2. 上传文件的事件钩子
- 监控上传进度
- upload 事件
- onloadstart 上传开始
- onprogress 数据传输进行中
- evt.total :需要传输的总大小;
- evt.loaded :当前上传的文件大小;
- onabort 上传操作终止
- onerror 上传失败
- onload 上传成功
- onloadend 上传完成(不论成功与否)
5-2-1. 显示上传信息
<body>
<input type="file" class="myfile" />
进度:<progress value="0" max="100"></progress> <span class="percent">0%</span>
速度:<span class="speed">20b/s</span>
<button>点击上传</button>
<button>取消上传</button>
</body>
<script>
let xhr = new XMLHttpRequest();
let btns = document.querySelectorAll("button");
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);
}
xhr.upload.onloadstart = function(){
console.log("开始上传");
}
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();
}
</script>
router.post("/fileUpload",(ctx,next)=>{
console.log(ctx.request.files);
ctx.body = "请求成功";
})
5-2-2. 实时显示上传进度
xhr.upload.onprogress = function (evt) {
let endTime = new Date().getTime();
let dtime = (endTime - stime) / 1000;
let dloaded = evt.loaded - sloaded;
let speed = dloaded / dtime;
stime = new Date().getTime();
sloaded = 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() + unit;
let percent = (evt.loaded / evt.total * 100).toFixed(0);
document.querySelector("progress").value = percent;
document.querySelector(".percent").innerHTML = percent + "%";
}
// 完整
<body>
<input type="file" class="myfile" />
进度:<progress value="0" max="100"></progress> <span class="percent">0%</span>
速度:<span class="speed">20b/s</span>
<button>点击上传</button>
<button>取消上传</button>
</body>
<script>
let xhr = new XMLHttpRequest();
let btns = document.querySelectorAll("button");
btns[0].onclick = function () {
let file = document.querySelector(".myfile").files[0];
let form = new FormData();
let stime;
let sloaded;
form.append("myfile", file);
xhr.open("post", "/fileUpload", true);
xhr.onload = function () {
console.log(xhr.responseText);
}
xhr.upload.onloadstart = function () {
console.log("开始上传");
stime = new Date().getTime();
sloaded = 0;
}
xhr.upload.onprogress = function (evt) {
let endTime = new Date().getTime();
let dtime = (endTime - stime) / 1000;
let dloaded = evt.loaded - sloaded;
let speed = dloaded / dtime;
stime = new Date().getTime();
sloaded = 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() + 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();
}
</script>
router.post("/fileUpload",(ctx,next)=>{
let fileData = fs.readFileSync(ctx.request.files.myfile.path);
fs.writeFileSync("practice/static/imgs/" + ctx.request.files.myfile.name, fileData);
ctx.body = "请求成功";
})