跳至主要內容

XMLHttpRequest

Weipeng Qiao大约 7 分钟

XMLHttpRequest

什么是 XMLHttpRequest 对象?

XMLHttpRequest(简称xhr)是浏览器提供的Javascript对象,通过它,可以***请求服务器上的数据资源***

使用 xhr 发起不带参数的 GET 请求

步骤:

  1. 创建 xhr 对象
  2. 调用 xhr.open()函数
  3. 调用 xhr.send()函数
  4. 监听 xhr.onreadystatechange 事件
// (1) 创建XHR对象
const xhr = new XMLHttpRequest();
// (2) 调用open()函数,指定请求方式与URL地址
xhr.open("GET", "http://www.liulongbin.top:3006/api/getbooks");
// (3) 调用send()函数,发起Ajax请求
xhr.send();
// (4) 监听 onreadystatechange事件
xhr.onreadystatechange = function () {
  // 4.1 监听xhr对象的请求状态 readyState;与服务器的响应状态 status
  if (xhr.readyState === 4 && xhr.status === 200) {
    //   4.2   打印服务器响应回来的数据
    console.log(xhr.responseText);
  }
};

xhr 对象的 readyState 属性

XMLHttpRequest 对象的**readystate 属性,用来表示当前的 Ajax 请求所处的状态**。每个 Ajax 请求必然处于一下状态中的一个:

值状态描述
0UNSENTXMLHttpRequet 对象已被创建,但尚未调用 open 方法
1OPENDopen 方法已经被调用
2HEADERS_RECEIVEDsend 方法已经被调用,响应头也已经被接收
3LOADING数据接收中,此时 response 属性已经包含部分数据
4DONEAjax 请求完成,这意味着数据已经彻底的**完成或者失败**

使用 xhr 发起带参数的 GET 请求

使用 xhr 对象发起带参数额 GET 请求时,只需要在调用 xhr.open 期间,为 URL 地址指定参数即可:

// ...省略不必要的代码
xhr.open("GET", "http://www.liulongbin.top:3006/api/getbooks?id=1");
// ...省略不必要的代码

这种在 URL 地址后面拼接的参数,叫做**查询字符串**。

什么是查询字符串

定义:查询字符串(URL 参数)是在 URL 的末尾加上用于向服务器发送信息的字符串(变量)。

格式:将英文的**?放在 URL 的末尾,然后再加上参数=值,想要加上多个参数的书啊,使用&**符号进行分割。以这个形式,可以将想要发送给服务器的内容添加到 URL 中。

示例:

            //不带参数的URL地址
            http: //www.liulongbin.top:3006/api/getbooks
            //带一个参数的URL地址
            http: //www.liulongbin.top:3006/api/getbooks?id=1
            //带两个参数的URL地址
            http: //www.liulongbin.top:3006/api/getbooks?id=1&bookname=西游记

GET 请求携带参数的本质

无论使用.ajax()或者.ajax()或者.get(),又或者直接使用 xhr 对象发起 GET 请求,当需要携带参数携带参数的时候,本质上都是直接将参数以查询字符串的形式,追加到 URL 地址的后面,发送到服务器的。

$.get(
  "url",
  {
    name: "zs",
    age: 20,
  },
  function () {}
);
//等价于
$get("url?name=zs&age=20", function () {});
$ajax({
  method: "GET",
  url: "url",
  data: {
    name: "zs",
    age: 20,
  },
  success: function () {},
});
//等价于
$ajax({
  method: "GET",
  url: "url?name=zs&age=20",
  success: function () {},
});

URL 编码和解码

URL 地址中,只允许出现英文相关的字母、标点符号、数字,因此,在 URL 地址中不允许出现中文字符。
如果 URL 中需要包含中文这样的字符,则必须对中文字符进行编码(转义)。
URL 编码的原则:使用安全的字符(没有特殊用途或者特殊意义的可打印字符)去表示那些不安全的字符。
URL 编码原则的通俗理解:使用英文字符去表示非英文字符。

            http: //www.liulongbin.top:3006/api/getbooks?id=1&bookname=西游记
            //经过URL编码之后,URL地址变成了如下格式:
            http: //www.liulongbin.top:3006/api/getbooks?id=1&bookname=%E8%A5%BF%E6%B8%B8%E8%AE%B0

如何对 URL 进行编码与解码

浏览器提供了 URL 编码与解码的 API,分别是:

  • encodeURI() 编码的函数
  • decodeURI() 解码的函数
//(1) 使用encodeURI()编码
console.log(encodeURI("js高级程序设计")); // 输出结果:js%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1
//(2) 使用decodeURI()解码
console.log(decodeURI("%E7%A8%8B%E5%BA%8F")); // 输出结果:程序

URL 编码的注意事项

由于浏览器会自动对 URL 地址进符编码 操作,因此,大多数情况下,程序员不需要关心 URL 地址的编码与解码操作。

使用 xhr 发起 POST 请求

// (1) 创建xhr对象
const xhr = new XMLHttpRequest();
// (2) 调用open()
xhr.open("POST", "http://www.liulongbin.top:3006/api/addbook");
// (3) 设置 Content-Type 属性(固定写法)
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
// (4) 调用 send() 同时将数据以查询字符串的形式,提交给服务器
xhr.send("bookname=你好啊&author=我很好&publisher=那就好");
// (5) 监听 onreadystatechange事件
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(xhr.responseText);
  }
};

XHR Level2 的新特性

可以设置 HTTP 请求的时限

const xhr = new XMLHttpRequest();

// 设置超时时间
xhr.timeout = 30;
// 设置超时以后的处理函数
xhr.ontimeout = function () {
  // 如果请求时长超过30毫秒,就打印请求超时了。
  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);
  }
};

可以使用 FormData 对象管理表单数据

Ajx 操作往往用来提交表单数据。为了方便表单处理,HTML5 新增了一个 FormData 对象,可以模拟表单操作:

FormData 的基本使用

// 1.创建FormData实例
const fd = new FormData();
// 2.调用append函数,向fd中追加数据
fd.append("uname", "steven");
fd.append("upwd", "123");
// 3. 创建 XHR对象
const xhr = new XMLHttpRequest();
// 4. 指定请求类型与URL地址
xhr.open("POST", "http://www.liulongbin.top:3006/api/formdata");

// 5.直接提交 FormData对象 ,这与提交网页表单效果完全一样
xhr.send(fd);
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(JSON.parse(xhr.responseText));
  }
};

使用 FormData 快速获取表单中的值

<body>
    <form id="form1">
        <input type="text" name="uname" autocomplete="off">
        <input type="password" name="upwd">
        <button type="submit">提交</button>
    </form>
    <script>
        const form = document.querySelector('#form1');
        form.addEventListener('submit', function(e) {
            // 阻止表单默认提交行为
            e.preventDefault();
            // 创建FormData,快速获取到form表单中的数据
            const fd = new FormData(form);
            const xhr = new XMLHttpRequest();
            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>

可以上传文件

<body>
    <!-- 1.文件选择框 -->
    <input type="file" id="file1"> <br>
    <!-- 2.  上传文件的按钮 -->
    <button id="btnUpload">上传文件</button>
    <!-- 3. img标签,来显示上传成功以后的图片 -->
    <img src="" alt="" id="img" width="400px">


    <script>
        // 1.获取上传文件的按钮
        const btn = document.querySelector('#btnUpload');
        // 2.为按钮绑定单机处理事件函数
        btn.addEventListener('click', function() {
            // 3.获取到用户选择的文件列表
            const files = document.querySelector('#file1').files; // 因为files是一个数组,第一个文件就是files[0]
            if (files.length <= 0) { // 如果没有选择文件,就不要执行后续的操作,提示用户
                return alert('请选择要上传的文件!')
            }
            const fd = new FormData();
            // 将用户选择的文件添加到formData中
            fd.append('avater', files[0])

            const xhr = new XMLHttpRequest();
            xhr.open('POST', 'http://www.liulongbin.top:3006/api/upload/avatar');
            xhr.send(fd);
            xhr.onreadystatechange = function() {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    const data = JSON.parse(xhr.responseText);
                    if (data.status === 200) {
                        // 上传文件成功
                        // 将服务器返回的图片地址设置为<img>标签的src属性
                        document.querySelector('#img').src = 'http://www.liulongbin.top:3006' + data.url
                    }
                } else {
                    // 上传文件失败
                    console.log(data.message);
                }
            }
        })
    </script>
</body>

可以获得数据传输的进度信息(上传文件并显示上传进度)

// 引入bootstrap.css文件
<body>
    <div class="progress" style="width: 80%; margin: 20px auto 0;" id="js_bar">
        <div class="progress-bar progress-bar-striped active" style="width: 0%;" id="percent">
        </div>
    </div>
    <!-- 1.文件选择框 -->
    <input type="file" id="file1"> <br>
    <!-- 2.  上传文件的按钮 -->
    <button id="btnUpload">上传文件</button>
    <!-- 3. img标签,来显示上传成功以后的图片 -->
    <img src="" alt="" id="img" width="400px">


    <script>
        // 1.获取上传文件的按钮
        const btn = document.querySelector('#btnUpload');
        const bar = document.querySelector('#js_bar')

        // 2.为按钮绑定单机处理事件函数
        btn.addEventListener('click', function() {
            bar.style.display = 'block';
            // 3.获取到用户选择的文件列表
            const files = document.querySelector('#file1').files; // 因为files是一个数组,第一个文件就是files[0]
            if (files.length <= 0) {
                return alert('请选择要上传的文件!')
            }
            console.log('您选择了' + files.length + '个文件');
            const fd = new FormData();
            // 将用户选择的文件添加到formData中
            fd.append('avater', files[0])

            const xhr = new XMLHttpRequest();
            // 监听文件上传的进度
            xhr.upload.onprogress = function(e) {
                // e.lengthComputable 是一个boolean值,表示当前上传的资源是否具有可计算的长度
                if (e.lengthComputable) {
                    // e.loaded 已传输的字节
                    // e.total 需传输的总字节
                    // 计算出上传的进度
                    let procentComputable = Math.ceil((e.loaded / e.total) * 100);
                    const percent = document.querySelector('#percent');
                    percent.style.width = procentComputable + '%'; // 设置进度条宽度
                    percent.innerHTML = procentComputable + '%'; // 显示上传百分比
                    console.log('传输中' + procentComputable);
                    xhr.upload.onload = function() {
                        percent.className = 'progress-bar progress-bar-success' // 上传完成 进度条变绿色
                        setTimeout(function() {
                            bar.style.display = 'none'; // 隐藏进度条
                            procentComputable = 0; // 让百分比变为0
                            percent.innerHTML = ''; // 清空显示内容
                            percent.style.width = ''; // 清空进度条宽度
                            percent.className = 'progress-bar progress-bar-striped active'
                        }, 2000)

                    }
                }
            }
            xhr.open('POST', 'http://www.liulongbin.top:3006/api/upload/avatar');
            xhr.send(fd);
            xhr.onreadystatechange = function() {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    const data = JSON.parse(xhr.responseText);
                    console.log(data);
                    if (data.status === 200) {
                        // 上传文件成功
                        // 将服务器返回的图片地址设置为<img>标签的src属性
                        document.querySelector('#img').src = 'http://www.liulongbin.top:3006' + data.url
                    }
                } else {
                    // 上传文件失败
                    console.log(data.message);
                }

            }
        })
    </script>
</body>

上次编辑于:
贡献者: qiaoweipeng