前后端交互(一)——Ajax的使用

260 阅读4分钟

前后端交互(一)——Ajax的使用

课堂目标

  1. 理解ajax基本使用
  2. 会使用XMLHttpRequest对象实现数据交互
  3. 了解onreadystatechange服务器响应信息
  4. 会使用FormData对象上传文件
  5. 了解upload事件对象

知识要点

  1. ajax使用
  2. XMLHttpRequest对象
  3. FormData对象
  4. 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 = "请求成功"

})