12-前后端交互Ajax

本文深入讲解了Ajax的基础使用、参数详解及封装方法,并演示了如何利用FormData实现文件上传,包括显示上传信息和实时上传进度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. ajax

  • Ajax 即“Asynchronous Javascript And XML”(异步 JavaScript 和 XML)
  • 新建XMLHttpRequest对象

1-1. ajax的基本使用

let xhr = new XMLHttpRequest();
  • 配置请求参数
xhr.open("get","/checkUser",true); //true是异步,false是同步
  • 接收返还值
 xhr.onload = function(){
    let res = JSON.parse(xhr.responseText);
 }
  • 发送服务器
xhr.send();

1-2. 利用ajax来解决验证用户名

  • html发送处理请求
<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>
  • js处理请求
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");
  • json编码
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>
  • 响应
  • 需要引入koa-body来解析请求
const koaBody = require("koa-body");
app.use(koaBody({
    multipart: true
}));
router.post("/post", (ctx, next) => {

    // 获取前端通过post传来的数据
    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) => {
	// 设置xml类型便于传值后能被识别
    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);
        // 重写content-type
        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 = "请求成功";
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值