Ajax学习笔记(下)--使用XMLHttpRequest发送ajax

410 阅读7分钟

XMLHttpRequest的基本使用

  • 什么XMLHttpRequest

XMLHttpRequest(简称 xhr)是浏览器提供的 Javascript 对象,通过它,可以请求服务器上的数据资源。之前所学的 jQuery 中的 Ajax 函数,就是基于 xhr 对象封装出来的。

原生Ajax使用

get请求基本使用

  • 步骤:
    1. 创建xhr对象
    2. 调用open:设置请求方式和请求地址
    3. 调用send: 发送请求(这一步是异步操作)
    4. 设置onreadystatechange事件,监听请求的各种状态
      • readyState是xhr的属性,用来表示请求发送的状态
        • 取值为4代表下载完毕
        • 必须确保readyState为4才能使用响应的数据
      • xhr.status 代表请求是否成功
        • 200代表请求是成功的
      • xhr.responseText 代表接收的响应内容
	// 1 创建xhr对象
    var xhr = new XMLHttpRequest();
    // 2 调用open:设置请求方式和请求地址
    xhr.open('GET', 'http://www.liulongbin.top:3006/api/getbooks');
    // 3 调用send: 发送请求,这一步是异步操作
    xhr.send();

    // 4 设置事件,监听请求的各种状态
    //   - readyState是xhr的属性,用来表示请求发送的状态
    //     - 取值为4代表下载完毕
    //     - 必须确保readyState为4才能使用响应的数据
    //     - 进一步检测:
    //       - xhr.status 代表请求是否成功
    //          - 200代表请求是成功的
    xhr.onreadystatechange = function () {
      // 4.1 检测xhr.readyState取值和xhr.status取值
      if (xhr.readyState === 4 && xhr.status === 200) {
        // 4.2 接收响应的数据即可
        //    - 原生接收的响应内容是JSON格式,需要自己进行转换
        //      - jQuery会自动转换
        console.log(xhr.responseText);
      }
    };

了解xhr对象的readyState属性

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

readyState属性

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

 <script>
    // 1 创建xhr对象
    var xhr = new XMLHttpRequest();
    // 2 调用open:设置请求方式和请求地址
    //    - 如果希望设置get请求的请求参数,需要放置在open()参数2地址的最后位置
    //    - 书写方式为:  地址?名=值&名=值....
    //    - 名称说明: 名=值&名=值称为url编码格式  urlencoded
    xhr.open('GET', 'http://www.liulongbin.top:3006/api/getbooks?id=3&name=jack&age=18&gender=男');
    // 3 调用send: 发送请求,这一步是异步操作
    xhr.send();

    // 4 设置事件,监听请求的各种状态
    xhr.onreadystatechange = function () {
      // 4.1 检测xhr.readyState取值和xhr.status取值
      if (xhr.readyState === 4 && xhr.status === 200) {
        // 4.2 接收响应的数据即可
        console.log(xhr.responseText);
      }
    };
  </script>

? 开始后面叫 查询字符串

查询字符串

  • 什么是查询字符串

    • 定义:查询字符串(URL 参数)是指在 URL 的末尾加上用于向服务器发送信息的字符串(变量)。
    • 格式:将英文的 ? 放在URL 的末尾,然后再加上 参数=值 ,想加上多个参数的话,使用 & 符号进行分隔。以这个形式,可以将想要发送给服务器的数据添加到 URL 中。
	// 不带参数的 URL 地址
	http://www.HANGTIAN.top:3006/api/getbooks
	// 带一个参数的 URL 地址
	http://www.HANGTIAN.top:3006/api/getbooks?id=1
	// 带两个参数的 URL 地址
	http://www.HANGTIAN.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编码的原则:使用安全的字符(没有特殊用途或者特殊意义的可打印字符)去表示那些不安全的字符。 URL编码原则的通俗理解:使用英文字符去表示非英文字符。


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

  • 如何对URL进行编码与解码
    • 浏览器提供了 URL 编码与解码的 API,分别是:
      • encodeURI() 编码的函数
      • decodeURI() 解码的函数
      encodeURI('黑马程序员')
      // 输出字符串  %E9%BB%91%E9%A9%AC%E7%A8%8B%E5%BA%8F%E5%91%98
      decodeURI('%E9%BB%91%E9%A9%AC')
      // 输出字符串  黑马
      
      
  • URL编码的注意事项

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

使用xhr发起POST请求

  • 步骤:

① 创建 xhr 对象

② 调用 xhr.open() 函数

③设置 ContentType 属性\color{#FF3030}{③ 设置 Content-Type 属性}(固定写法)

④ 调用 xhr.send() 函数,同时指定要发送的数据\color{#FF3030}{同时指定要发送的数据}

⑤ 监听 xhr.onreadystatechange 事件

  • 使用xhr发起POST请求

<script>
   // 1 创建xhr对象
   var 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()
   //    - 如果具有请求参数,书写在send中,格式也是url编码格式(名=值&名=值...)
   xhr.send('bookname=老人与海&author=海明威&publisher=大海图书出版社');

   // 5 设置readystatechange事件,接收响应的数据
   xhr.onreadystatechange = function () {
     if (xhr.readyState === 4 && xhr.status === 200) {
       console.log(xhr.responseText);
     }
   };
 </script>

get与post发送方式的区别

  • 请求参数的书写位置不同:
    • get请求方式:在xhr.open()的url后面使用?连接
    • post请求:在xhr.send()中书写
  • post请求需要设置Content-Type

数据交换格式

  • 什么是数据交换格式

数据交换格式,就是服务器端与客户端之间进行数据传输与交换的格式。 前端领域,经常提及的两种数据交换格式分别是 XML 和 JSON。其中 XML 用的非常少,所以,我们重点要学习的数据交换格式就是 JSON。

JSON

  • 什么是JSON

    • 概念:JSON 的英文全称是 JavaScript Object Notation,即“JavaScript 对象表示法”。简单来讲,JSON 就是 Javascript 对象和数组的字符串表示法,它使用文本表示一个 JS 对象或数组的信息,因此,JSON 的本质是字符串。
    • 作用:JSON 是一种轻量级的文本数据交换格式,在作用上类似于 XML,专门用于存储和传输数据,但是 JSON 比 XML 更小、更快、更易解析。
    • 现状:JSON 是在 2001 年开始被推广和使用的数据格式,到现今为止,JSON 已经成为了主流的数据交换格式。
  • JSON的两种结构

JSON 就是用字符串来表示 Javascript 的对象和数组。所以,JSON 中包含对象\color{#FF3030}{对象}数组\color{#FF3030}{数组}两种结构,通过这两种结构的相互嵌套,可以表示各种复杂的数据结构。

  • 对象结构

对象结构\color{#FF3030}{对象结构}:对象结构在 JSON 中表示为 { } 括起来的内容。数据结构为 { key: value, key: value, … } 的键值对结构。其中,key 必须是使用英文的双引号包裹\color{blue}{英文的双引号包裹}的字符串,value 的数据类型可以是数字、字符串、布尔值、null、数组、对象\color{blue}{数字、字符串、布尔值、null、数组、对象} 6种类型。

{
    name: "zs",
    'age': 20,
    "gender": '男',
    "address": undefined,
    "hobby": ["吃饭", "睡觉", '打豆豆']
    say: function() {}
}


{
    "name": "zs",
    "age": 20,
    "gender": "男",
    "address": null,
    "hobby": ["吃饭", "睡觉", "打豆豆"]
}

  • 数组结构

数组结构\color{#FF3030}{数组结构}:数组结构在 JSON 中表示为 [ ] 括起来的内容。数据结构为 [ "java", "javascript", 30, true … ] 。数组中数据的类型可以是数字、字符串、布尔值、null、数组、对象\color{blue}{数字、字符串、布尔值、null、数组、对象} 6种类型。


	[ "java", "python", "php" ]
	[ 100, 200, 300.5 ]
	[ true, false, null ]
	[ { "name": "zs", "age": 20}, { "name": "ls", "age": 30} ]
	[ [ "苹果", "榴莲", "椰子" ], [ 4, 50, 5 ] ]

  • JSON语法注意事项

    ① 属性名必须使用双引号包裹

    ② 字符串类型的值必须使用双引号包裹

    ③ JSON 中不允许使用单引号表示字符串

    ④ JSON 中不能写注释

    ⑤ JSON 的最外层必须是对象或数组格式

    ⑥ 不能使用 undefined 或函数作为 JSON 的值

JSON的作用\color{red}{JSON 的作用}:在计算机与网络之间存储和传输数据。

JSON的本质\color{#FF3030}{JSON 的本质}:用字符串来表示 Javascript 对象数据或数组数据

JSON和JS对象的关系

JSON 是 JS 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。例如:

	
    	 //这是一个对象
		 var obj = {a: 'Hello', b: 'World'}

		//这是一个 JSON 字符串,本质是一个字符串
		var json = '{"a": "Hello", "b": "World"}' 
    

JSON和JS对象的互转

  • 要实现从 JSON 字符串转换为 JS 对象,使用 JSON.parse() 方法:
var obj = JSON.parse('{"a": "Hello", "b": "World"}')
//结果是 {a: 'Hello', b: 'World'}
  • 要实现从 JS 对象转换为 JSON 字符串,使用 JSON.stringify() 方法
var json = JSON.stringify({a: 'Hello', b: 'World'})
//结果是 '{"a": "Hello", "b": "World"}'

序列化和反序列化

  • \color{blue}{数据对象}$$\color{red}{转换为}$$\color{blue}{字符串}的过程,叫做序列化\color{red}{序列化},例如:调用 JSON.stringify\color{red}{stringify}() 函数的操作,叫做 JSON 序列化。
  • \color{blue}{字符串}$$\color{red}{转换为}$$\color{blue}{数据对象}的过程,叫做反序列化\color{red}{反序列化},例如:调用 JSON.parse\color{red}{parse}() 函数的操作,叫做 JSON 反序列化。

封装ajax函数



	<script>
    // 封装用来发送ajax请求的函数
    //  参数:
    //    对象
    //      - type 请求方式
    //      - url 请求地址
    //      - data 请求参数
    //      - success 回调函数
    function ajax(options) {
      // 1 创建xhr对象
      var xhr = new XMLHttpRequest();
      // 2 设置请求的相关信息: 需要根据请求方式分别设置
      //   - 统一保存数据
      var data = urlencoded(options.data);
      //  2.1 将请求方式统一转换为大写(或小写)
      var type = options.type.toUpperCase();
      //  2.2 对请求方式进行检测
      if (type === 'GET') {
        xhr.open('GET', options.url + '?' + data);
        xhr.send();
      } else if (type === 'POST') {
        xhr.open('POST', options.url);
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.send(data);
      }
      // 3 设置事件接收响应内容
      xhr.onreadystatechange = function () {
        // 3.1 检测
        if (xhr.readyState === 4 && xhr.status === 200) {
          // 3.2 将JSON格式的字符串转换为对象
          var res = JSON.parse(xhr.responseText);
          options.success(res);
        }
      };
    }

 </script>


ajax level2新功能: 超时时间设置

  • 设置方式:

    • 属性: xhr.timeout = 超时时间; // 毫秒单位
    • 时间: xhr.ontimeout = function() { 请求超时后,触发的事件 };
    
    	<script>
    		var xhr = new XMLHttpRequest();
    		xhr.open('GET', 'http://www.liulongbin.top:3006/api/getbooks');
    
    		// 设置请求的超时时间(毫秒单位)
    		xhr.timeout = 500;
    
    		// 设置事件,对超时进行处理
    		xhr.ontimeout = function () {
      	 	console.log('网络开小差了,请稍后再试!~');
    		};
    
    		xhr.send();
    		xhr.onreadystatechange = function () {
      		if (xhr.readyState === 4 && xhr.status === 200) {
       			 console.log(xhr.responseText);
     	 		}
     		};
    
    	</script>
    
    

FormData的使用

  • 作用:
    • 可以快速处理表单的数据
    • 可以进行文件上传
  • 注意点:
    • 发送FormData需要使用POST请求方式
    • 不需要单独设置Content-Type
<body>
  <!-- 设置form标签,用来进行formdata操作 -->
  <form id="testForm">
    <!-- 如果要表单元素数据被请求发送,必须设置name -->
    <input type="text" name="username">
    <input type="password" name="password">
    <!-- 设置按钮,点击后,使用FormData处理表单数据 -->
    <button type="button" id="btn">ajax提交</button>
  </form>


  <script>
    // 1 空FormData对象,添加数据后发送
    // 1.1 创建FormData对象
    var fd = new FormData(); // 空的FormData对象
    // 1.2 向fd中添加一些数据
    fd.append('name', 'jack');
    fd.append('age', 18);

    // 1.3 使用POST方法将fd发送到对应接口(此处的接口为formdata)
    //  - 无需设置之前post的content-type
    var xhr = new XMLHttpRequest();
    xhr.open('POST', 'http://www.liulongbin.top:3006/api/formdata');
    xhr.send(fd); // 将fd放入在send()参数中即可
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4 && xhr.status === 200) {
        console.log(xhr.responseText);
      }
    };

    // 2 通过FormData对象管理表单元素,再发送(同时也可以添加数据)
    document.getElementById('btn').onclick = function () {
      // 2.1 创建FormData对象,传入DOM对象形式的form标签到参数中
      var testForm = document.getElementById('testForm');
      var fd = new FormData(testForm); // 空的FormData对象

      // 2.1.1 也可以在存在form的基础上,自己添加数据
      fd.append('name', 'rose');
      fd.append('age', 21);

      // 2.3 使用POST方法将fd发送到对应接口(此处的接口为formdata)
      //  - 无需设置之前post的content-type
      var xhr = new XMLHttpRequest();
      xhr.open('POST', 'http://www.liulongbin.top:3006/api/formdata');
      xhr.send(fd); // 将fd放入在send()参数中即可
      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4 && xhr.status === 200) {
          console.log(xhr.responseText);
        }
      };
    }

  </script>
</body>


使用FormData上传文件


<body>

  <input type="file" id="iptFile">
  <button type="button" id="btn">点击上传文件</button>
  <img src="" alt="" id="pic">

  <script>
    /*
      步骤:
        1 准备结构(文件域)
        2 检测用户是否选择了文件
        3 使用FormData保存文件信息
        4 通过ajax发送
        5 响应处理,展示服务端获取到的图片
    */

    // 1 给按钮设置点击事件
    var btn = document.getElementById('btn');
    var iptFile = document.querySelector('#iptFile');
    var pic = document.getElementById('pic');
    btn.onclick = function () {
      // 2 检测是否上传了文件,需要检测文件域的files属性
      //   - files是一个伪数组,可以通过判断length,检测是否选择了文件
      console.dir(iptFile);

      if (iptFile.files.length === 0) {
        // 说明没有选择文件
        alert('请选择文件后提交!~');
        return;
      }

      // 3 通过FormData保存文件信息
      var fd = new FormData();
      //  - avatar是接口中规定的请求参数名称
      //  - files[0]代表选择的文件的相关信息
      fd.append('avatar', iptFile.files[0]);

      // 4 发送post形式的ajax,将fd发送给服务端处理
      var xhr = new XMLHttpRequest();
      xhr.open('POST', 'http://www.liulongbin.top:3006/api/upload/avatar');
      xhr.send(fd);

      // 5 接收响应数据
      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4 && xhr.status === 200) {
          var res = JSON.parse(xhr.responseText);
          // 5.1 检测上传是否成功
          if (res.status === 200) {
            // 5.2 将响应的图片在线地址,放入到img中展示
            pic.src = 'http://www.liulongbin.top:3006' + res.url
          }

        }
      }
    };

  </script>
</body>

上传进度条功能

  • xhr.upload.onprogress 上传中实时触发事件
    • 事件对象e的属性
      • e.lengthComputable 文件长度使用可用
      • e.loaded 以上传大小
      • e.total 总大小
  • xhr.upload.onload 上传完毕时触发事件

	<!DOCTYPE html>
	<html lang="en">

	<head>
  		<meta charset="UTF-8">
  		<meta name="viewport" content="width=device-width, initial-scale=1.0">
  		<meta http-equiv="X-UA-Compatible" content="ie=edge">
  		<title>Document</title>
  		<link rel="stylesheet" href="./lib/bootstrap.css">
  		<script src="./lib/jquery.js"></script>
	</head>

<body>
  <input type="file" id="iptFile">
  <button type="button" id="btn">提交</button>
  <!-- 设置进度条需要的结构 -->
  <!-- 这个div是进度条外面的盒子 -->
  <div class="progress" style="width:500px;margin:10px 0;">
    <!-- 这个div是会变长的内部的盒子 -->
    <div class="progress-bar progress-bar-info progress-bar-striped active" id="percent" style="width:0%">0%</div>
  </div>

  <script>
    /*
      1 检测上传过程的事件
        xhr.upload.onprogress = function(e) ...
      2 检测文件大小是否可用
        e.lengthComputable 布尔值
      3 计算进度
        e.loaded 已加载的大小
        e.total  总大小
      3.1 计算方式
        e.loaded / e.total 上传的进度

      制作进度条功能的思路:
        - 设置内外两个盒子,外部盒子定宽度,内部盒子宽度默认为0
        - 上传中,根据上传的进度设置内部盒子宽度对应改变即可

      xhr.upload.onload 上传完毕后触发的事件
    */

    var btn = document.getElementById('btn');
    var iptFile = document.querySelector('#iptFile');
    var pic = document.getElementById('pic');
    btn.onclick = function () {
      // 2 检测是否上传了文件,需要检测文件域的files属性
      if (iptFile.files.length === 0) {
        alert('请选择文件后提交!~');
        return;
      }

      // 3 通过FormData保存文件信息
      var fd = new FormData();
      fd.append('avatar', iptFile.files[0]);

      // 4 发送post形式的ajax,将fd发送给服务端处理
      var xhr = new XMLHttpRequest();

      // 设置上传文件的进度监测
      xhr.upload.onprogress = function (e) {
        // 判断e.lengthComputable是否为true,为true表示可以进行计算
        if (e.lengthComputable) {
          // 根据e.loaded和e.total计算进度比例
          var bili = e.loaded / e.total * 100 + '%';
          console.log(e);
          document.getElementById('percent').style.width = bili;
          document.getElementById('percent').innerText = bili;
        }
      };

      // 设置上传文件完成的事件(更改样式)
      xhr.upload.onload = function () {
        document.getElementById('percent').className = 'progress-bar progress-bar-success';
      }


      xhr.open('POST', 'http://www.liulongbin.top:3006/api/upload/avatar');
      xhr.send(fd);

      // 5 接收响应数据
      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4 && xhr.status === 200) {
          var res = JSON.parse(xhr.responseText);
          // 5.1 检测上传是否成功
          if (res.status === 200) {
            // 5.2 将响应的图片在线地址,放入到img中展示
            console.log(res);

          }

        }
      }
    };
  </script>
</body>

</html>