• Ajax——运行原理和实现、异步编程、函数封装


    一、Ajax基础

    传统网站中存在的问题

    • 网速慢的情况下,页面加载时间长,用户只能等待
    • 表单提交后,如果一项内容不合格,需要重新填写所有表单内容
    • 页面跳转,重新加载页面,造成资源浪费,增加用户等待时间

    1.1 Ajax概述

    他是浏览器提供的一套方法,可以实现页面无刷新更新数据,在不刷新页面的情况下和服务器进行交互,

    主要目的:提高用户浏览器网站应用的体验

    1.2 Ajax的应用场景

    • 1. 页面上拉加载更多数据
    • 2. 列表数据无刷新分页
    • 3. 表单项离开焦点数据验证
    • 4. 搜索框提示文字下拉列表

    1.3 Ajax的运行环境

    Ajax技术需要运行在网站环境中才能生效,也就是说要能以localhost域名的方式打开页面,不能直接双击HTML文件打开


    二、Ajax运行原理及实现

    2.1 运行原理:

    实现步骤:

            //1.创建Ajax对象
            var xhr = new XMLHttpRequest();
            //2. 告诉Ajax对象要向哪发送请求,以什么方式发送请求
            //1)请求方式  2)请求地址(其实就是服务器短的路由地址)
            xhr.open('get', 'http://localhost:3000/first');
            //3.发送请求
            xhr.send();
            //4.获取服务器端响应到客户端的数据
            // 由于服务器返回数据的时间是不确定的,我们需要绑定一个onload事件,当xhr对象接受到服务器返回的数据之后,自动触发onload事件
            xhr.onload = function(){
                // xhr对象下面的responseText存储的就是响应的数据
                console.log(xhr.responseText)
            }

      

    2.2 服务器端响应的数据格式:

    在真实项目中,服务器端大多数情况下会以JSON对象作为响应数据的格式,当客户端拿到响应数据时,要把JSON数据和HTML字符串进行拼接,然后将拼接的结果展示在页面中

    在http 请求和响应的过程中,无论是请求参数还是响应内容,如果是对象类型,最终都会被转换为对象字符串进行传输

    JSON.parse(参数) //将JSON字符串转换为JSON对象,JSON是window对象下面的一个属性

    服务器端返回的json格式的数据到了客户端这边就变成了json格式的字符串,

    • 需要先转换为json对象
    • 然后和HTML拼接
    • 再使用DOM方法放到页面中即可
    var responseText = JSON.parse(xhr.responseText)  //将JSON字符串转换为JSON对象
    var str = '<h2>'+ responseText.name +'</h2>';   // 和HTML拼接
    document.body.innerHTML = str                // 使用DOM方法放到页面中

     2.3 请求参数传递

    • GET请求方式
    xhr.open('get', 'http://www.example.com?'+params);
    //获取用户在文本框中输入的值
    var nameValue = username.value;
    var ageValue = age.value;
    
    // 要拼接成 username=123&age=456
    var params = 'username='+ nameValue +'&age=' +ageValue;
    
    // 配置Ajax对象
    xhr.open('get', 'http://localhost:3000/get?'+ params);
    • POST请求方式

    post请求方式必须设置请求报文

    报文:在http请求和响应的过程中传递的数据块就叫做报文,报文包括报文头和报文体,这些数据和信息要遵守规定好的格式

    报文头:存储一些键值对信息,可以理解为客户端向服务端说的一些话

    报文体:主要存储一些信息,比如post请求参数就是存储在报文体中

    报文头和报文体在请求的过程中一起发送到服务器

    1.post请求参数时,拼接出来的参数一定要放在send方法中, 然后就相当于把请求参数放到了请求体中

    2.必须明确请求参数的格式类型

    //获取用户在文本框中输入的值
    var nameValue = username.value;
    var ageValue = age.value;
    
    // 要拼接成 username=123&age=456
    var params = 'username='+ nameValue +'&age=' +ageValue;
    
    // 配置Ajax对象
    xhr.open('post', 'http://localhost:3000/post');
    
    // 设置请求参数的格式类型 xhr.setRequestHeader(
    'Content-type','application/x-www-form-urlencoded'); // 发送请求 xhr.send(params);

    在服务器端的app.js需要添加以下代码

    // post请求需要的第三方包,从而可以使用req.body
    const bodyParser = require('body-parser');
    
    // 解析 application/x-www-form-urlencoded 这种格式的请求参数
    app.use(bodyParser.urlencoded());
    
    app.post('/post', (req, res) =>{
        // 通过req.body 获取客户端传递给服务器端的post请求参数
        res.send(req.body);
    })

    2.4 传递json格式的请求参数

    请求参数的格式有两种

    1. application/x-www-form-urlencoded

    name=zhangsan&age=26&sex=男

    2. application/json

    {name: 'zhangsan', age:'20', sex:'男'}

    当传递json格式类型的请求参数的时候

    1. 在请求头中指定 Content-Type 属性的值为 application/json,告诉服务器端当前请求参数的格式是json

    xhr.setRequestHeader('Content-Type','application/json');

    2. json格式类型的参数必须使用POST请求方式,get请求不能提交json对象数据格式,传统网站表单提交也不支持json对象数据格式

    3.将json对象转换为json字符串进行传递

    JSON.stringify({name:'lisa', age:50})

    4. 在服务器端解析请求参数的格式需要引入第三方包,并且针对不同的格式设置不同的解析方式

    // post请求需要的第三方包
    const bodyParser = require('body-parser');
    // 解析 application/x-www-form-urlencoded 这种格式的请求参数
    app.use(bodyParser.urlencoded());
    // 解析 application/json 这种格式的请求参数
    app.use(bodyParser.json());
    

    // 字符串拼接格式 app.post('/post', (req, res) => { res.send(req.body); }); // json格式 app.post('/json', (req, res) => { res.send(req.body); });

    2.5 获取服务器端的响应

    Ajax状态码:在创建Ajax对象、配置Ajax对象、发送请求一级接受完服务器端响应数据,这个过程中的每一个步骤都会对应一个数值,这个数值就是Ajax状态码

    0:请求未初始化(还没有调用open())

    1:请求已经建立,但是还没有发送(还没有调用send())

    2:请求已经发送

    3:请求正在处理中,通常响应中已经有部分数据可以用了

    4:响应已经完成,可以获取并使用服务器的响应了

    xhr.readyState // 获取Ajax状态码

    两种获取服务器端响应方式的区别

     推荐使用onload,除非需要兼容IE低版本

    2.6 Ajax错误处理 

     

    Ajax状态码:表示Ajax请求过程状态 Ajax对象返回的

    http状态码:表示请求的处理结果,是服务器端返回的

    2.7 低版本IE浏览器的缓存问题

    问题:在低版本的IE浏览器中,Ajax请求有严重的缓存问题,即在请求地址不发生变化的情况下,只有第一次请求会真正的发送到服务器端,后续的请求都会从浏览器的缓存中获取结果,即使服务器端的数据更新了,客户端依然拿到的是缓存中的旧数据

    解决方案:在请求地址的后面加请求参数,保证每一次请求中的请求参数的值不相同 

    xhr.open('get', 'http://www.example.com?t=' + Math.random());

    三、Ajax异步编程

    3.1 

    同步:代码逐行完成,一行代码完成之后才执行下一行代码

    异步:一个人事情做了一二班,转而去做其他事情,当其他事情做完之后,再回过头来继续做之前未完成的事情

    异步代码虽然需要花时间去执行,但程序不会等待异步代码执行完成之后再继续执行后续代码,而是直接执行后续代码,当后续代码执行完成后再回头看异步代码是否返回结果,如果已有返回结果,再调用事先准备好的回调函数处理异步代码执行结果

    事件处理函数就是一个回调函数

    xhr.onload = function(){
                console.log(2)
                // xhr对象下面的responseText存储的就是响应的数据
                console.log(xhr.responseText)
            }
    console.log(1)
    // 会先输出1,在输出2

    3.2 Ajax封装

    问题:发送一次请求代码过多,发送多次请求代码冗余且重复

    解决方案:将请求的代码封装到函数中,发送请求时调用函数即可

    (1)最基本的封装

    function ajax(options){
            //创建ajax对象
            var xhr = new XMLHttpRequest();
            //配置ajax对象
            xhr.open(options.type, options.url);
            // 发送请求
            xhr.send();
            // 获取响应结果,监听xhr对象下面的onload事件
    // 当xhr对象接受完响应数据后被触发 xhr.onload = function(){
    // 对于下面返回的服务器结果,我们想把这个数据放到函数体的外面,让用户决定它的使用方式,在下面传递的对象中创建了一个success函数
    // 在这里调用success函数,并且把请求的结果传递到函数中作为形参 options.success(xhr.responseText); } }
    // 上面封装了一个ajax函数,下面调用这个函数,并且传递了一个实参,实参中包含了请求方式和请求地址 ajax({ // type代表请求方式 type: 'get', // 请求地址 url: 'http://localhost:3000/first', // success这个函数在请求成功之后被调用,请求成功也就是执行onload事件的时候就代表请求成功,也就是在onload事件处理函数被触发就代表请求成功, // 下面这个函数是一个定义,data是一个形参,接受服务器响应结果 success: function(data){ console.log('这里是success函数'+data); } })

    (2)发送请求时需要传递参数

    要考虑的问题:

    • 请求参数位置的问题:将请求参数传递到ajax函数内部,在函数内部根据请求方式的不同将请求参数放置在不同的位置

              get  放在请求地址的后面;post  放在send方法中

    • 请求参数格式的问题:         
    application/x-www-form-urlencoded
           参数名称=参数值&参数名称=参数值
           name=zhangsan&age=20
    application/json
           {name: 'zhangsan', age: 20}

    1. 传递对象数据类型对于函数的调用者更加友好

    2. 在函数内部对象数据类型转换为字符串数据类型更加方便

    <script type="text/javascript">
        function ajax(options){
    
            // 存储的是默认值,在调用这个函数的时候,我们不希望每次都传递大量参数,因此设置一个默认值
            // 如果传递了这个参数,就覆盖掉默认值,否则使用默认值
            var defaults = {
                type:'get',
                url:'',
                data:{},
                header:{
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                success:function(){},
                error:function(){}
            };
    
            // 使用options 对象中的属性覆盖defaults 对象中的属性
            Object.assign(defaults, options);
    
            //创建ajax对象
            var xhr = new XMLHttpRequest();
            // 拼接请求参数的变量
            var params = '';
            // 循环用户传递进来的对象格式参数
            for(var attr in defaults.data){
                params += attr + '=' + defaults.data[attr] + '&';
            }
            // 将参数最后面的 & 截取掉,并将结果重新赋值给params
            params = params.substr(0,params.length-1);
    
            // 判断请求方式
            if(defaults.type == 'get'){
                defaults.url = defaults.url + '?' + params;
            }
    
            //配置ajax对象
            xhr.open(defaults.type, defaults.url);
            // 如果请求方式为post
            if(defaults.type == 'post'){
                // 用户希望的向服务器段传递的请求参数的类型
                var contentType = defaults.header['Content-Type']
                // post请求需要明确 设置请求方式格式的类型
                xhr.setRequestHeader('Content-Type', contentType);
    
                // 如果传递的是json类型的参数,只能使用post请求方式
                if(contentType == 'application/json'){
                    // 将json对象转换为json字符串进行传递
                    xhr.send(JSON.stringify(defaults.data))
                }else{
                    // 向服务器传递普通类型的请求参数
                    xhr.send(params);
                }
            }else{
                // 如果请求方式是get
                xhr.send();
            }
            // 获取响应结果,监听xhr对象下面的onload事件
            xhr.onload = function(){
    // 使用ajax技术向服务器发送请求的时候,一般都会返回json类型的数据,但是客户端拿到的是json字符串
    // 要想使用这个数据就需要把字符串转换为json对象,我们想把这个过程封装到函数当中,这样函数的调用者就不需要关心这个细节了,拿到的就直接是json对象
    // 但是在函数内部定义这个函数的时候,我们不知道服务器端返回的数据是什么类型的,通过xhr.getResponseHeader获取返回的数据,Content-Type就代表返回的数据类型
                // xhr.getResponseHeader()
                // 获取响应头当中的数据
                var contentType = xhr.getResponseHeader('Content-Type');
    
                // 服务器端返回的数据
                var responseText = xhr.responseText
    
                // 如果响应类型中包含application/json
                if(contentType.includes('application/json')){
                    // 将json字符串转换为json对象
                    responseText = JSON.parse(responseText)
                }
    
                // 当http状态码等于200的时候
                if(xhr.status == 200){
                    // 请求成功,调用处理成功情况的函数
                    // xhr对象中包含很多信息,因此可以吧xhr也传递出去
                    defaults.success(responseText, xhr);
                }else{
                    // 请求失败 调用处理失败情况的函数
                    defaults.error(responseText, xhr);
                }
                
            }
    
        }
    
        // 上面封装了一个ajax函数,下面调用这个函数,并且传递了一个实参,实参中包含了请求方式和请求地址
        ajax({
            // type代表请求方式
            type: 'get',
            // 请求地址
            url: 'http://localhost:3000/responseData',
            data: {
                name: 'zhangsan',
                age: 20
            },
            header: {
                'Content-Type': 'application/json'
            },
            // success这个函数在请求成功之后被调用,请求成功也就是执行onload事件的时候就代表请求成功,也就是在onload中调用success函数
            // 下面这个函数是一个定义,data是一个形参
            success: function(data){
                console.log('这里是success函数');
                console.log(data)
            },
            error: function(data, xhr){
                console.log('这里是error函数,'+ data);
                console.log(xhr)
            }
        })
        </script>
  • 相关阅读:
    spring boot使用自定义注解+AOP实现对Controller层指定方法的日志记录
    spring事务管理中,注解方式和xml配置方式优先级谁高?
    synchronized修饰类中不同方法,调用的时候方法互斥吗
    java(spring boot)实现二维码生成(可以插入中间log和底部文字)
    java借助Robot给微信好友自动发消息(可发送表情包)
    js中Map类型的使用
    【转】Intellij笔记
    Tomcat6.0webappsevopWEB-INFclasses (系统找不到指定的路径)
    多线程多进程之其他
    文件操作
  • 原文地址:https://www.cnblogs.com/ccv2/p/12785921.html
Copyright © 2020-2023  润新知