前端学习从入门到高级全程记录之45 (ajax---1)

学习目标

本期开始我们学习Ajax。

AJAX

1. 概述

Web程序最初的目的就是将信息(数据)放到公共的服务器,让所有网络用户都可以通过浏览器访问。

在这里插入图片描述
在此之前,我们可以通过以下几种方式让浏览器发出对服务端的请求,获得服务端的数据:

地址栏输入地址,回车,刷新 
特定元素的 href 或 src 属性 
表单提交

这些方案都是我们无法通过或者很难通过代码的方式进行编程(对服务端发出请求并且接受服务端返回的响应), 如果我们可以通过 JavaScript 直接发送网络请求,那么 Web 的可能就会更多,随之能够实现的功能也会更多,至 少不再是“单机游戏”。

A JAX(Asynchronous JavaScript and XML),最早出现在 2005 年的 Google Suggest,是在浏览器端进行网络编 程(发送请求、接收响应)的技术方案,它使我们可以通过 JavaScript 直接获取服务端最新的内容而不必重新加载 页面。让 Web 更能接近桌面应用的用户体验。

说白了,A JAX 就是浏览器提供的一套 API(方法),可以通过 JavaScript 调用,从而实现通过代码控制请求与响应。实现 网络编程。

能力不够 API 凑。

2.快速上手

使用 A JAX 的过程可以类比平常我们访问网页过程

// 1. 创建一个 XMLHttpRequest 类型的对象 —— 相当于打开了一个浏览器
2	var xhr = new XMLHttpRequest()
3	// 2. 打开与一个网址之间的连接 —— 相当于在地址栏输入访问地址
4	xhr.open('GET', './time.php')
5	// 3. 通过连接发送一次请求 —— 相当于回车或者点击访问发送请求
6	xhr.send(null)
7	// 4. 指定 xhr 状态变化事件处理函数 —— 相当于处理网页呈现后的操作
8	xhr.onreadystatechange = function () {
9	// 通过 xhr 的 readyState 判断此次请求的响应是否接收完成
10	if (this.readyState === 4) {
11	// 通过 xhr 的 responseText 获取到响应的响应体
12	console.log(this)
13	}
14	}

练习:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>AJAX 的快速上手(发送请求)</title>
</head>
<body>
  <script>
    // 涉及到 AJAX 操作的页面“不能”使用文件协议访问(文件的方式访问)

    // AJAX 是一套 API 核心提供的类型:XMLHttpRequest
    // 1. 安装浏览器(用户代理)
    //  xhr 就类似于浏览器的作用(发送请求接收响应)
    var xhr = new XMLHttpRequest()
    // 2. 打开浏览器 输入网址
    xhr.open('GET', 'http://day-11.io/time.php')
    // 3. 敲回车键 开始请求
    xhr.send()
    // 4. 等待响应

    // 5. 看结果

  </script>
</body>
</html>

快速上手(接受响应):

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>AJAX 的快速上手(接收响应)</title>
</head>
<body>
  <script>

    var xhr = new XMLHttpRequest()

    // 如果需要补货第一个状态的变化,需要注意代码执行顺序的问题(不要出现来不及的情况)
    // xhr.onreadystatechange = function () {
    //   // 这个事件并不是只在响应时触发,状态改变就触发
    //   // console.log(1)
    //   console.log(this.readyState)
    // }

    xhr.open('GET', './time.php')

    xhr.send()

    // 因为客户端永远不知道服务端何时才能返回我们需要的响应
    // 所以 AJAX API 采用事件的机制(通知的感觉)
    xhr.onreadystatechange = function () {
      // 这个事件并不是只在响应时触发,XHR 状态改变就触发
      // console.log(1)
      if (this.readyState !== 4) return
      // 获取响应的内容(响应体)
      console.log(this.responseText)
    }

    // 因为响应需要时间,所以无法通过返回值的方式返回响应
    // var response = xhr.send()
    // console.log(response)

  </script>
</body>
</html>

2.1. readyState

由于 readystatechange 事件是在 xhr 对象状态变化时触发(不单是在得到响应时),也就意味着这个事件会被触发多次,所以我们有必要了解每一个状态值代表的含义:

readyState状态描述说明
0UNSENT代理(XHR)被创建,但尚未调用 open() 方法
1OPENEDopen() 方法已经被调用,建立了连接
2HEADERS_RECEIVEDsend() 方法已经被调用,并且已经可以获取状态行和响应头。
3LOADING响应体下载中, responseText 属性可能已经包含部分数据。
4DONE响应体下载完成,可以直接使用 responseText 。
2.1.1. 时间轴

1	var xhr = new XMLHttpRequest()
2	console.log(xhr.readyState) 3	// => 0
4	// 初始化 请求代理对象
5
6	xhr.open('GET', 'time.php')
7	console.log(xhr.readyState) 8	// => 1
9	// open 方法已经调用,建立一个与服务端特定端口的连接
10
11	xhr.send() 12
13	xhr.addEventListener('readystatechange', function () {
14	switch (this.readyState) {
15	case 2:
16	// => 2
17	// 已经接受到了响应报文的响应头
18
19	// 可以拿到头
20	// console.log(this.getAllResponseHeaders())
21	console.log(this.getResponseHeader('server')) 22	// 但是还没有拿到体
23	console.log(this.responseText)
24	break
25
26	case 3:
27	// => 3
28	// 正在下载响应报文的响应体,有可能响应体为空,也有可能不完整
29
30	// 在这里处理响应体不保险(不可靠)
31	console.log(this.responseText)
32	break
33
34	case 4:
35	// => 4
36	// 一切 OK (整个响应报文已经完整下载下来了)
37
38	// 这里处理响应体
39	console.log(this.responseText)
40	break
41	}
42	})

通过理解每一个状态值的含义得出一个结论:一般我们都是在 readyState 值为 4 时,执行响应的后续逻辑。

1	xhr.onreadystatechange = function () {
2	if (this.readyState === 4) { 3		// 后续逻辑......
4	}
5	}

readyState状态变化:
readystate.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>readyState</title>
</head>
<body>
  <script>

    var xhr = new XMLHttpRequest()
    console.log(xhr.readyState)
    // => 0
    // 初始化 请求代理对象

    xhr.open('GET', 'time.php')
    console.log(xhr.readyState)
    // => 1
    // open 方法已经调用,建立一个与服务端特定端口的连接

    xhr.send()

    xhr.addEventListener('readystatechange', function () {
      switch (this.readyState) {
        case 2:
          // => 2
          // 已经接受到了响应报文的响应头

          // 可以拿到头
          // console.log(this.getAllResponseHeaders())
          console.log(this.getResponseHeader('server'))
          // 但是还没有拿到体
          console.log(this.responseText)
          break

        case 3:
          // => 3
          // 正在下载响应报文的响应体,有可能响应体为空,也有可能不完整
          // 在这里处理响应体不保险(不可靠)
          console.log(this.responseText)
          break

        case 4:
          // => 4
          // 一切 OK (整个响应报文已经完整下载下来了)
          console.log(this.responseText)
          break
      }
    })

  </script>
</body>
</html>

2.2. 遵循 HTTP

本质上 XMLHttpRequest 就是 JavaScript 在 Web 平台中发送 HTTP 请求的手段,所以我们发送出去的请求任然是
HTTP 请求,同样符合 HTTP 约定的格式:

// 设置请求报文的请求行
xhr.open('GET', './time.php')
// 设置请求头
xhr.setRequestHeader('Accept', 'text/plain')
// 设置请求体
xhr.send(null)

xhr.onreadystatechange = function () {
if (this.readyState === 4) {
// 获取响应状态码
console.log(this.status)
// 获取响应状态描述
console.log(this.statusText)
// 获取响应头信息 console.log(this.getResponseHeader('Content‐Type')) // 指定响应头 console.log(this.getAllResponseHeader()) // 全部响应头
// 获取响应体
console.log(this.responseText) // 文本形式
console.log(this.responseXML) // XML 形式,了解即可不用了
}
}

参考链接:
https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest

https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest

3.具体用法

3.1. GET 请求

通常在一次 GET 请求过程中,参数传递都是通过 URL 地址中的  ? 参数传递。
var xhr = new XMLHttpRequest()
// GET 请求传递参数通常使用的是问号传参
// 这里可以在请求地址后面加上参数,从而传递数据到服务端
xhr.open('GET', './delete.php?id=1')
// 一般在 GET 请求时无需设置响应体,可以传 null 或者干脆不传
xhr.send(null)
xhr.onreadystatechange = function () { if (this.readyState === 4) {
console.log(this.responseText)
}
}

// 一般情况下 URL 传递的都是参数性质的数据,而 POST 一般都是业务数据

ajax-get.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>AJAX发送GET请求并传递参数</title>
</head>
<body>
  <ul id="list"></ul>

  <script>

    var listElement = document.getElementById('list')

    // 发送请求获取列表数据呈现在页面
    // =======================================

    var xhr = new XMLHttpRequest()

    xhr.open('GET', 'users.php')

    xhr.send()

    xhr.onreadystatechange = function () {
      if (this.readyState !== 4) return
      var data = JSON.parse(this.responseText)
      // data => 数据

      for (var i = 0; i < data.length; i++) {
        var liElement = document.createElement('li')
        liElement.innerHTML = data[i].name
        liElement.id = data[i].id

        listElement.appendChild(liElement)

        liElement.addEventListener('click', function () {
          // TODO: 通过AJAX操作获取服务端对应数据的信息
          // 如何获取当前被点击元素对应的数据的ID
          // console.log(this.id)
          var xhr1 = new XMLHttpRequest()
          xhr1.open('GET', 'users.php?id=' + this.id)
          xhr1.send()
          xhr1.onreadystatechange = function () {
            if (this.readyState !== 4) return
            var obj = JSON.parse(this.responseText)
            alert(obj.age)
          }
        })
      }
    }

    // 给每一个 li 注册点击事件
    // 因为 li 后来动态创建,所以不能这样注册事件
    // for (var i = 0; i < listElement.children.length; i++) {
    //   listElement.children[i].addEventListener('click', function () {
    //     console.log(111)
    //   })
    // }


    // var xhr = new XMLHttpRequest()
    // // 这里任然是使用URL中的问号参数传递数据
    // xhr.open('GET', 'users.php?id=2')
    // xhr.send(null)

    // xhr.onreadystatechange = function () {
    //   if (this.readyState !== 4) return
    //   console.log(this.responseText)
    // }

  </script>
</body>
</html>

user.php:

<?php

header('Content-Type: application/json');
/**
 * 返回的响应就是一个 JSON 内容(返回的就是数据)
 * 对于返回数据的地址一般我们称之为接口(形式上是 Web 形式)
 */

// `/users.php?id=1` => id 为 1 的用户信息

$data = array(
  array(
    'id' => 1,
    'name' => '张三',
    'age' => 18
  ),
  array(
    'id' => 2,
    'name' => '李四',
    'age' => 20
  ),
  array(
    'id' => 3,
    'name' => '二傻子',
    'age' => 18
  ),
  array(
    'id' => 4,
    'name' => '三愣子',
    'age' => 19
  )
);


if (empty($_GET['id'])) {
  // 没有 ID 获取全部
  // 因为 HTTP 中约定报文的内容就是字符串,而我们需要传递给客户端的信息是一个有结构的数据
  // 这种情况下我们一般采用 JSON 作为数据格式
  $json = json_encode($data); // => [{"id":1,"name":"张三"},{...}]
  echo $json;
} else {
  // 传递了 ID 只获取一条
  foreach ($data as $item) {
    if ($item['id'] != $_GET['id']) continue;
    $json = json_encode($item); // => [{"id":1,"name":"张三"},{...}]
    echo $json;
  }
}

在这里插入图片描述

3.2. POST 请求

POST 请求过程中,都是采用请求体承载需要提交的数据。

1   var xhr = new XMLHttpRequest()
2	// open 方法的第一个参数的作用就是设置请求的 method
3	xhr.open('POST', './add.php')
4	// 设置请求头中的 Content‐Type 为 application/x‐www‐form‐urlencoded 5	// 标识此次请求的请求体格式为 urlencoded 以便于服务端接收数据
6	xhr.setRequestHeader('Content‐Type', 'application/x‐www‐form‐urlencoded') 7	// 需要提交到服务端的数据可以通过 send 方法的参数传递
8	// 格式:key1=value1&key2=value2
9	xhr.send('key1=value1&key2=value2')
10	xhr.onreadystatechange = function () {
11	if (this.readyState === 4) {
12	console.log(this.responseText) 13	}
14	}

例子:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>AJAX发送POST请求</title>
  <style>
    #loading {
      display: none;
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background-color: #555;
      opacity: .5;
      text-align: center;
      line-height: 300px;
    }

    #loading::after {
      content: '加载中...';
      color : #fff;
    }
  </style>
</head>
<body>
  <div id="loading"></div>
  <table border="1">
    <tr>
      <td>用户名</td>
      <td><input type="text" id="username"></td>
    </tr>
    <tr>
      <td>密码</td>
      <td><input type="password" id="password"></td>
    </tr>
    <tr>
      <td></td>
      <td><button id="btn">登录</button></td>
    </tr>
  </table>
  <script>

    // 找一个合适的时机,做一件合适的事情
    var btn = document.getElementById('btn')
    // 1. 获取界面上的元素 value
    var txtUsername = document.getElementById('username')
    var txtPassword = document.getElementById('password')
    var loading = document.getElementById('loading')

    btn.onclick = function () {
      loading.style.display = 'block'
      var username = txtUsername.value
      var password = txtPassword.value
      // 2. 通过 XHR 发送一个 POST 请求
      var xhr = new XMLHttpRequest()
      xhr.open('POST', 'login.php')
      // !!! 一定注意 如果请求体是 urlencoded 格式 必须设置这个请求头 !!!
      xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
      // xhr.send('username=' + username + '&password=' + password)
      xhr.send(`username=${username}&password=${password}`)
      // 3. 根据服务端的反馈 作出界面提示
      xhr.onreadystatechange = function () {
        if (this.readyState !== 4) return
        console.log(this.responseText)
        loading.style.display = 'none'
      }
    }

  </script>
</body>
</html>

同步与异步

关于同步与异步的概念在生活中有很多常见的场景,举例说明。

同步:一个人在同一个时刻只能做一件事情,在执行一些耗时的操作(不需要看管)不去做别的事,只是等  待

异步:在执行一些耗时的操作(不需要看管)去做别的事,而不是等待

xhr.open() 方法第三个参数要求传入的是一个 bool 值,其作用就是设置此次请求是否采用异步方式执行,默认
为 true ,如果需要同步执行可以通过传递 false 实现:

console.log('before ajax')
var xhr = new XMLHttpRequest()
// 默认第三个参数为 true 意味着采用异步方式执行
xhr.open('GET', './time.php', true)
xhr.send(null)
xhr.onreadystatechange = function () {
if (this.readyState === 4) {
// 这里的代码最后执行
console.log('request done')
}
}
console.log('after ajax')

如果采用同步方式执行,则代码会卡死在 xhr.send() 这一步:

console.log('before ajax')
var xhr = new XMLHttpRequest()
// 同步方式
xhr.open('GET', './time.php', false)
// 同步方式 执行需要 先注册事件再调用 send,否则 readystatechange 无法触发
xhr.onreadystatechange = function () {
if (this.readyState === 4) {
// 这里的代码最后执行
console.log('request done')
}
}
xhr.send(null)
console.log('after ajax')

演示同步异步差异。
一定在发送请求 send() 之前注册 readystatechange (不管同步或者异步):
为了让这个事件可以更加可靠(一定触发),一定是先注册。

了解同步模式即可,切记不要使用同步模式。

至此,我们已经大致了解了 AJAX 的基本 API 。

练习:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <script>

    // console.time('abc')
    // for (var i = 0; i < 100000000; i++) {}
    // console.timeEnd('abc')

    // console.log('begin request')
    var xhrAsync = new XMLHttpRequest()
    // open 方法的第三个参数是 async 可以传入一个布尔值,默认为 true
    xhrAsync.open('GET', 'time.php', true)
    console.time('async')
    xhrAsync.send()
    console.log(xhrAsync.responseText)
    // console.log('end request')
    console.timeEnd('async')

    // 同步模式 ajax 操作会有等待的情况
    // 区别在于 send 方法会不会出现等待情况
    // console.log('begin request')
    var xhrSync = new XMLHttpRequest()
    // open 方法的第三个参数是 async 可以传入一个布尔值,默认为 true
    xhrSync.open('GET', 'time.php', false)
    console.time('sync')
    xhrSync.send()
    console.log(xhrSync.responseText)
    // console.log('end request')
    console.timeEnd('sync')

  </script>
</body>
</html>

time.php:

<?php

// for ($i = 0; $i < 100000; $i++) {
//   echo time();
// }

echo time();

在这里插入图片描述

同步模式注册事件时机问题

先来做一个练习:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>同步模式的AJAX</title>
	</head>
	<body>
		<script>
			var xhr=new XMLHttpRequest()
			xhr.open('GET','time.php',false)
			xhr.send()
			xhr.onreadystatechange=function() {
				console.log(this.readyState)
			}
		</script>
	</body>
</html>

在这里插入图片描述
你会发现,之前open参数为true的时候会打印2、3、4,现在为false却完全不打印了。

原因:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>同步模式的AJAX</title>
	</head>
	<body>
		<script>
			var xhr=new XMLHttpRequest()
			xhr.open('GET','time.php',false)
			xhr.send()//等到请求响应的过程全部完成再继续
			console.log(xhr.readyState)//这时候已经等于4了
			xhr.onreadystatechange=function() {//这时候注册onreadystatechange已经没意义了,它在一个值变成另外一个值的时候才会触发。
				console.log(this.readyState)
			}
		</script>
	</body>
</html>

这就是注册时机太晚。所以要想onreadystatechange触发,必须在send之前。即:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>同步模式的AJAX</title>
	</head>
	<body>
		<script>
			var xhr=new XMLHttpRequest()
			xhr.open('GET','time.php',false)
			xhr.onreadystatechange=function() {
				console.log(this.readyState)
			}
			xhr.send()//等到请求响应的过程全部完成再继续
			console.log(xhr.readyState)
			
		</script>
	</body>
</html>

那为什么设置为true就能打印出4了呢?是因为true表示异步,send方法就是发一个信号,完成是需要一些时间的,此时注册事件同步进行,发送完,事件也执行完了,所以能打印出4.

3.4. 响应数据格式

提问:如果希望服务端返回一个复杂数据,该如何处理?

关心的问题就是服务端发出何种格式的数据,这种格式如何在客户端用 JavaScript 解析

3.4.1. XML

一种数据描述手段 老掉牙的东西,简单演示一下,不在这里浪费时间,基本现在的项目不用了。

淘汰的原因:数据冗余太多

xml.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>AJAX 请求 XML 格式的数据</title>
</head>
<body>
  <script>

    var xhr = new XMLHttpRequest()
    xhr.open('GET', 'xml.php')
    xhr.send()
    xhr.onreadystatechange = function () {
      if (this.readyState !== 4) return
      // this.responseXML 专门用于获取服务端返回的 XML 数据,操作方式就是通过 DOM 的方式操作
      // 但是需要服务端响应头中的 Content-Type 必须是 application/xml
      console.log(this.responseXML.documentElement.children[0].innerHTML)
      console.log(this.responseXML.documentElement.getElementsByTagName('name')[0])
    }

  </script>
</body>
</html>

xml.php:

<?php
header('Content-Type: application/xml');
?>
<?xml version="1.1" encoding="utf-8"?>
<person>
  <name>张三</name>
  <age>18</age>
  <gender>男</gender>
</person>

3.4.2. JSON

也是一种数据描述手段,类似于 JavaScript 字面量方式

服务端采用 JSON 格式返回数据,客户端按照 JSON 格式解析数据。

不管是 JSON 也好,还是 XML,只是在 A JAX 请求过程中用到,并不代表它们之间有必然的联系,它们只是 数据协议罢了

3.5. 处理响应数据渲染

模板引擎:

artTemplate:https://aui.github.io/art-template/

模板引擎实际上就是一个 API,模板引擎有很多种,使用方式大同小异,目的为了可以更容易的将数据渲染到 HTML中。

3.6 兼容方案

XMLHttpRequest 在老版本浏览器(IE5/6)中有兼容问题,可以通过另外一种方式代替

var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')

3.7 关于进程和线程

进程与线程:
进程
进行中的程序
线程
线程就是拿着剧本(代码)去演戏的演员,专业上是CPU最小执行单元

总结

本期学习到此结束。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本课程详细讲解了以下内容:    1.jsp环境搭建及入门、虚拟路径和虚拟主机、JSP执行流程    2.使用Eclipse快速开发JSP、编码问题、JSP页面元素以及request对象、使用request对象实现注册示例    3.请求方式的编码问题、response、请求转发和重定向、cookie、session执行机制、session共享问题     4.session与cookie问题及application、cookie补充说明及四种范围对象作用域     5.JDBC原理及使用Statement访问数据库、使用JDBC切换数据库以及PreparedStatement的使用、Statement与PreparedStatement的区别     6.JDBC调用存储过程和存储函数、JDBC处理大文本CLOB及二进制BLOB类型数据     7.JSP访问数据库、JavaBean(封装数据和封装业务逻辑)     8.MVC模式与Servlet执行流程、Servlet25与Servlet30的使用、ServletAPI详解与源码分析     9.MVC案例、三层架构详解、乱码问题以及三层代码流程解析、完善Service和Dao、完善View、优化用户体验、优化三层(加入接口和DBUtil)    1 0.Web调试及bug修复、分页SQL(Oracle、MySQL、SQLSERVER)     11.分页业务逻辑层和数据访问层Service、Dao、分页表示层Jsp、Servlet     12.文件上传及注意问题、控制文件上传类型和大小、下载、各浏览器下载乱码问题     13.EL表达式语法、点操作符和中括号操作符、EL运算、隐式对象、JSTL基础及set、out、remove     14.过滤器、过滤器通配符、过滤器链、监听器     15.session绑定解绑、钝化活化     16.以及Ajax的各种应用     17. Idea环境下的Java Web开发

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值