JavaScript之Ajax(三):使用XMLHttpRequest与axios库发起请求

1、XMLHttpRequest的基本使用

XMLHttpRequest简称xhr,是浏览器提供的JS对象,可以请求服务器上的数据资源,之前学习的到JQ中的ajax函数,就是基于xhr对象封闭出来的。
(1)使用xhr发起get请求
    <script>
        //1.创建xhr对象
        var xhr = new XMLHttpRequest();
        //2.用xhr.open()函数
        xhr.open('get','http://www.liulongbin.top:3006/api/getbooks');
        //3.用xhr.send()函数
        xhr.send();
        //4.监听xhr.onreadystatechange事件
        xhr.onreadystatechange = function() {
            if(xhr.readyState === 4 && xhr.status ===200) {
                console.log(xhr.responseText);
            }
        }
    </script>
说明:
xhr.readyState表示当前ajax请求所处的状态,有0,1,2,3,4等4种状态,分别是创建对象、open、send、接收中、完成。
url后面可以拼接?id=1等等参数,这个参数叫‘查阅字符串“。如果要添加多个参数,可以用&并联起来。例如,?id=1,?id=1&bookname=西游记等。
$.ajax() $.get() xhr对象等发起请求,当要携带参数时,本质上是直接把参数以以查询字符串的形式,追加到URL的后面,发送给服务器。
    <script>
        // $.get('http://www.liulongbin.top:3006/api/getbooks',{id:1,bookname:'西游记'},function(e) {
        //     console.log(e);
        // })
        $.ajax({
            method: 'get',
            url: 'http://www.liulongbin.top:3006/api/getbooks',
            data: {id: 2},
            success: function(res) {
                console.log(res);
            }
        })
    </script>
  URL中一般只当放英文的字母数字标点符号,如果出现了中文等符号,则必须进行编辑(转义)。一个汉字对应3组百分号。编码的原则,用安全的字符表示哪些不安全的字符。可以使用encodeUR() decodeURI()进行编码、解码。
      <script>
        var str = '白龙网';
        var str2 = encodeURI(str);
        console.log(str2);
        var str3 = decodeURI('%E7%99%BD');
        console.log(str3);
    </script>
(2) 使用POST发起请求
    <script>
        // 1、创建xhr对象
        var xhr = new XMLHttpRequest();
        // 2、调用xhr.open()函数
        xhr.open('post','http://www.liulongbin.top:3006/api/addbook');
        // 3、设置content-type属性
        xhr.setRequestHeader('content-type','application/x-www-form-urlencoded');
        // 4、调用xhr.send(),同时会指定发送的数据
        xhr.send('bookname=水浒传&author=施耐庵&publisher=上海图书出版社');
        // 5、监听xhr.onreadystatechange事件
        xhr.onreadystatechange = function() {
            if(xhr.readyState === 4 && xhr.status ===200) {
                console.log(xhr.responseText);
            }
        }
    </script>

2、数据交换模式

 数据交换的格式,就是服务器与客户端之间进行的数据传输也交换的格式。前端领域涉及的格式分别是XML,JSON。
 服务器存储数据与资源,客户端接收资源与数据,此时需要数据传输与交换XML/JSON,对前端而言,JSON用的最多。
 什么是XML?可扩展的标记语言,与HTML类似,但是两者之间没有任何关系。
 HTML是网页内容的载体,XML用来传递数据,是数据的载体。XML的缺点,格式臃肿,和数据无关的代码多,体积大,传输效率低;解析XML麻烦。
 什么是JSON,JS对象表示法,它是JS对象与数组的字符串表示,本质是字符串。作用是轻量级的文本数据交换格式,更小,更快,易于解析。成为主流的数据交换格式。
 JSON包含两种结构,对象、数组。
 JSON对象结构,用花括号包括起来的内容,数据结构为{"key":value},键值是用双引号包括的字符串,value是数据类型可以是数字、字符串、布尔值、null、数组、对象等六种类型。如果value是字符串,必须用双绰号包裹。value不允许出现undefined和function。 
注意事项:
属性名必须使用双引号
字符患上 类型的值也必须双引号
不能写注释
最外层必须是对象或者数组格式
不能使用undefined。
JSON的作用是在计算机与网络之间传输数据,其本质是一个字符串。
JSON是JS对象的字符串表示法,它使用文本表示一个JS对象的信息,本质是一个字符串。
(1)JSON与JS对象相互转换。JSON.parse()  JSON.stringify(obj)
    <script>
        var jsonStr = '{"siteName":"白龙网","siteTime":"从建站到SEO的在线服务平台"}';
        //1.把JSON字符串转换成对象
        var obj = JSON.parse(jsonStr);
        console.log(obj);
        //2.把对象转换成JSON字符串
        var jsonstr2 = JSON.stringify(obj);
        console.log(jsonstr2);
    </script>
  (2)服务器响应数据
  通过一个案例,可以看出,从服务器拿到的响应数据是一个JSON字符串,这个字符串可以转换成数组,或者转换成对象,方便渲染到页面。
    <script>
        var xhr = new XMLHttpRequest();
        xhr.open('get','http://www.liulongbin.top:3006/api/getbooks');
        xhr.send();
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4 && xhr.status ===200) {
                console.log(xhr.responseText);
                console.log(JSON.parse(xhr.responseText));
            }
        }
    </script>
把数据对象转换为字符串的过程,叫序列化,例如JSON.stringify()操作,叫做JSON的序列化;反之,把字符串转换为对象的过程,叫反序列化。

3、封装ajax函数

 把data对象转换为查询字符串的方式,提交给服务器
<script>
        //1.封装数据处理函数
        //把用户提交的对象数据转换为查询字符串:即遍历对象时,拼接对象的每个属性名和属性值并放进空数组,然后把数据中的所有元素拼接起来
        function postData(data) {
            var arr = [];
            for (var k in data) {
                var str = k + '=' + data[k];
                arr.push(str);
            }
            return arr.join('&');
        }
        //2.封装自己的ajax函数
        function bailong(options) {
            var xhr = new XMLHttpRequest();
            var qs = postData(options.data);
        //判断请求类型
        if(options.method.toUpperCase() === 'GET') {
            //发起get请求
            xhr.open(options.method,options.url + '?' + qs);
            xhr.send();
        } else if (options.method.toUpperCase() === 'POST') { 
            //发起post请求
            xhr.open(options.method,options.url);
            xhr.setRequestHeader('content-type','application/x-www-form-urlencoded');
            xhr.send(qs)
        }
        xhr.onreadystatechange = function() {
            if(xhr.readyState === 4 && xhr.status ===200) {
              var results = JSON.parse(xhr.responseText);
              options.success(results);
            }
        }
        }
        //3.请求数据成功
        // bailong({
        //     method: 'GET',
        //     url: 'http://www.liulongbin.top:3006/api/getbooks',
        //     // data: {id:1},
        //     success: function(e) {
        //         console.log(e);
        //     }
        // })
        //4.提交数据成功
        bailong({
            method: 'POST',
            url: 'http://www.liulongbin.top:3006/api/addbook',
            data: {
                bookname: '白龙网',
                author: '京城小白龙',
                publisher: '白龙出版社'
            },
            success: function (e) {
                console.log(e);
            }
        })
    </script>

4、XMLHttpRequest level2新特性

旧版XHR只支持文本数据的传输,无法用来读取和上传文件;传输和接收数据时,没有进度信息,只能提示有没有完成。
新版本的XHR,可设置HTTP请求的时限;可以使用formData对象管理表单数据;可以上传文件;可以获取数据传输的进度信息。
(1)xhr.timeout超时属性
设置HTTP请求时限:timeout属性设置HTTP请求时限
xhr.timeout = 3000,没有单位,默认毫秒。
与之配套的,还有一个timeout事件用来指定回调函数。
xhr.ontimeout = function() {
  alert(请求超时)
}
    <script>
       var xhr = new XMLHttpRequest()
       //1.设置超时时间
        xhr.timeout = 3
       //2.设置超时事件
        xhr.ontimeout = function() {
            console.log('超时了');
        }
       xhr.open('get','http://www.liulongbin.top:3006/api/getbooks')
       xhr.send()
       xhr.onreadystatechange = function() {
        if(xhr.readyState === 4 && xhr.status === 200) {
            console.log(xhr.responseText);
        }
       }
    </script
(2)使用FormData对象管理表单数据
    <script>
       //1.创建formData实例
        var fd = new FormData()
       //2.调用append()函数,向实例添加数据
       fd.append('uname','bailong')
       fd.append('psd','123456')
       //3.创建xhr对象
        var xhr = new XMLHttpRequest()
       //4.指定请求类型与URL
        xhr.open('POST','http://www.liulongbin.top:3006/api/formdata')
       //5.提交formdata对象
        xhr.send(fd)
       //6.监听响应数据
        xhr.onreadystatechange = function() {
            if(xhr.readyState === 4 && xhr.status === 200) {
                var res = JSON.parse(xhr.responseText)
                console.log(res);
            }
        }
    </script>
(3)使用FormData对象获取网页表单的值
<body>
    <form id="form1">
        <input type="text" name="uname" id="uname" autocomplete="off">
        <input type="password" name="psd" id="psd">
        <input type="submit" value="提交" id="submit">
    </form>
    <script>
       //获取数据
       var form = document.querySelector('#form1')
       //监听submit按钮
       form.addEventListener('submit',function(e) {
        //阻止默认行为
        e.preventDefault()
        //把form表单数据存入fd
        var fd = new FormData(form)
        //创建xhr对象
        var xhr = new XMLHttpRequest()
        //指出请求方式、URL
        xhr.open('POST','http://www.liulongbin.top:3006/api/formdata')
        //发送数据
        xhr.send(fd)
        //监听服务器响应数据
        xhr.onreadystatechange = function() {
            if(xhr.readyState === 4 && xhr.status === 200) {
                console.log(JSON.parse(xhr.responseText))
            }
        }
       })
    </script>
</body>
(4)使用FormData对象实现上传文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./bootstrap-3.4.1-dist/css/bootstrap.min.css">
    <script src="./jquery-3.6.0/jquery-3.6.0.min.js"></script>
</head>
<body>
    <!-- 文件上传 -->
    <input type="file" name="file" id="file1">
    <!-- 提交 -->
    <input type="submit" value=" 提交" id="filesupload">
    <!-- 进度条 -->
    <div class="progress" style="width: 500px;margin:15px 10px">
        <div class="progress-bar progress-bar-striped active" style="width: 0%" id="percent">
          0%
        </div>
      </div>
    <!-- 显示上传的内容 -->
    <br>
    <img src="" alt="" id="img1" width="200">
    <script>
        //1.获取上传按钮元素
       var filesupload = document.querySelector('#filesupload');
       //2.监听上传按钮点击事件
       filesupload.addEventListener('click',function() {
        //3.获取用户上传文件内容
            var files = document.querySelector('#file1').files;
            if(files.length <= 0) {
                return alert('请选择上传的文件');
            }
            //创建formdata
            var fd = new FormData();
            //追加文件
            fd.append('avatar',files[0]);
            //使用xhr发起上传文件的请求
            var xhr = new XMLHttpRequest();
            //上传进度功能实现
            xhr.upload.onprogress = function(e) {
                //e.lengthComputable返回返回值是布尔值,判断是否可计算
                if(e.lengthComputable) {
                //已上传字节数/总字节数
                var percentcompute = Math.ceil( (e.loaded / e.total) * 100);
                console.log(percentcompute);
                $('#percent').attr('style','width:' + percentcompute + '%;').html(percentcompute + '%');
                };
            }
            //监听上传完成后事件,改变成绿色
            xhr.upload.onload = function() {
                $('#percent').removeClass().addClass('progress-bar progress-bar-success')
            }
            xhr.open('post','http://www.liulongbin.top:3006/api/upload/avatar');
            xhr.send(fd);
            // 监听服务器响应数据
            xhr.onreadystatechange = function() {
            if(xhr.readyState === 4 && xhr.status === 200) {
                var data = JSON.parse(xhr.responseText);
                console.log(data);
                if(data.status === 200) {
                    //上传成功
                    document.querySelector('#img1').src = 'http://www.liulongbin.top:3006' + data.url;
                } else {
                    //上传失败
                    console.log('上传失败' + data.message);
                }
            }
            }
       })
    </script>
</body>
</html>
(5)使用jQuery实现文件上传功能
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./bootstrap-3.4.1-dist/css/bootstrap.min.css">
    <script src="./jquery-3.6.0/jquery-3.6.0.min.js"></script>
</head>
<body>
    <input type="file" id="file1">
    <button id="btnupload">提交</button>
    <br>
    <img src="./loading.gif" alt="" id="loading" style="display:none">
    <script>
        //监听ajax发起了就显示图片
        $(document).ajaxStart(function() {
            $('#loading').show();
        })
        //监听ajax结束了就隐藏图片
        $(document).ajaxStop(function() {
            $('#loading').hide();
        })
        $(function() {
            //1.判断是否上传了文件
            $('#btnupload').on('click',function() {
                var files = $('#file1')[0].files;
                if(files.length <= 0) {
                    return alert('请上传文件再上传');
                }
                console.log('ok');
            //把上传的数据放到FormData
            var fd = new FormData();
            fd.append('avatar',files[0]);
            //发起JQuery请求,使用ajax请求
            $.ajax({
                method: 'post',
                url: 'http://www.liulongbin.top:3006/api/upload/avatar',
                data: fd,
                contentType: false,//上传文件必须指定
                processData: false,//上传文件必须指定
                success: function(e) {
                    console.log(e);
                }
            })
            })
        })
    </script>
</body>
</html>

5、axios库

专注于网络数据请求的库,与XMLHttpRequest而言,axios库简单易用,与jQuery相比,axios更加轻量化,只专注于网络数据请求。
(1)axios发起get请求
在写js之前,先在head区域导入axios.js库
<body>
    <button id="btn">发起get请求</button>
    <script>
        document.querySelector('#btn').addEventListener('click',function() {
            var url = 'http://www.liulongbin.top:3006/api/get';
            var paramsobj = {name: 'bailong',age: 18};
            //axios.get().then(function(){})
            axios.get(url,{params:paramsobj}).then(function(e) {
                //服务器响应的数据是e.data
                console.log(e.data);
            })
        })
    </script>
</body>
(2)axios发起post请求
同理,在使用axios.js库之前,要先引入
<body>
    <button id="btn">发起post请求</button>
    <script>
        document.querySelector('#btn').addEventListener('click',function() {
            var url = 'http://www.liulongbin.top:3006/api/post';
            var paramsobj = {name: 'bailong',age: 18};
            //axios.post().then(function(){})
            axios.post(url,paramsobj).then(function(e) {
                //服务器响应的数据是e.data
                console.log(e.data);
            })
        })
    </script>
</body>
(3)使用axios发起请求:get/post
<body>
    <button id="btnget">使用axios直接发起请求GET</button>
    <button id="btnpost">使用axios直接发起请求GET</button>
    <script>
        //axios发起get请求
        document.querySelector('#btnget').addEventListener('click',function() {
            var url = 'http://www.liulongbin.top:3006/api/get';
            var paramsobj = {name: '白龙SEO',age: 28};
            axios({
                method: 'get',
                url: url,
                params: paramsobj
            }).then(function(e) {
                console.log(e.data);
            })
        })
        //axios发起post请求
        document.querySelector('#btnpost').addEventListener('click',function() {
            var url = 'http://www.liulongbin.top:3006/api/post';
            var data = {name: '白龙drupal',age: 66};
            axios({
                method: 'post',
                url: url,
                data: data
            }).then(function(e) {
                console.log(e.data);
            })
        })
    </script>
</body>