前端学习从入门到高级全程记录之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 | 状态描述 | 说明 |
---|---|---|
0 | UNSENT | 代理(XHR)被创建,但尚未调用 open() 方法 |
1 | OPENED | open() 方法已经被调用,建立了连接 |
2 | HEADERS_RECEIVED | send() 方法已经被调用,并且已经可以获取状态行和响应头。 |
3 | LOADING | 响应体下载中, responseText 属性可能已经包含部分数据。 |
4 | DONE | 响应体下载完成,可以直接使用 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)breakcase 3:// => 3// 正在下载响应报文的响应体,有可能响应体为空,也有可能不完整// 在这里处理响应体不保险(不可靠)console.log(this.responseText)breakcase 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) returnvar data = JSON.parse(this.responseText)// data => 数据for (var i = 0; i < data.length; i++) {var liElement = document.createElement('li')liElement.innerHTML = data[i].nameliElement.id = data[i].idlistElement.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) returnvar 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:
<?phpheader('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. 获取界面上的元素 valuevar txtUsername = document.getElementById('username')var txtPassword = document.getElementById('password')var loading = document.getElementById('loading')btn.onclick = function () {loading.style.display = 'block'var username = txtUsername.valuevar 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) returnconsole.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 可以传入一个布尔值,默认为 truexhrAsync.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 可以传入一个布尔值,默认为 truexhrSync.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/xmlconsole.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最小执行单元
总结
本期学习到此结束。
前端学习从入门到高级全程记录之45 (ajax---1)相关推荐
- 前端学习从入门到高级全程记录之1 (HTML基础知识)
本次学习目标 掌握HTML的基本知识,能够写出简单的页面. 1. 开发工具 1.浏览器(chrome(谷歌浏览器),IE浏览器,火狐浏览器,QQ浏览器等等). 2.代码编辑器(sublime,HBui ...
- 前端学习从入门到高级全程记录之41 (PHP基础Ⅳ)
学习目标 本期接上期内容继续学习php基础知识. HTTP 1. 概要 1.1. 定义 HTTP(HyperText Transfer Protocol,超文本传输协议)最早就是计算机与计算机之间沟通 ...
- 前端学习从入门到高级全程记录之16(CSS高级技巧)
本期目标 本期我们继续学习一些CSS的高级技巧. 1.CSS W3C 统一验证工具 CssStats 是一个在线的 CSS 代码分析工具,可以分析你写的代码到底好不好,哪里出错. 网址是: http: ...
- 前端学习从入门到高级全程记录之8 (PS基本使用综合案例)
本期学习目标 本期我们将学习PS的基本使用并且运用以前所学的CSS和HTML的技术来完成一个综合案例. 1.Photoshop基本使用 早在第一期我就让大家去安装PS,不知道安装了没有,没有安装的尽快 ...
- 前端学习从入门到高级全程记录之12 (CSS高级技巧)
学习目标 本期主要学习字体,内容较少.下一期我们将做一个京东的项目,用到的知识将会非常多,非常的有综合性. 1.Web字体 字体格式 不同浏览器所支持的字体格式是不一样的,我们有必要了解一下有关字体格 ...
- 前端学习从入门到高级全程记录之11 (云道页面例子后续)
本期目标 本期学习的主要目标是:1.前几期"云道页面例子"的完善.2.CSS高级技巧的学习 1.云道页面完善 在前2期的云道案例中,我们完成了一半的布局,后面的内容需要用到定位等知 ...
- 前端学习从入门到高级全程记录之35(jQuery②)
学习目标 本期继续学习jQuery,引入的jQuery文件用的还是上一期的. 1.jQuery操作样式 1.1 css操作 功能:设置或者修改样式,操作的是style属性 设置单个样式 //nam ...
- 前端学习从入门到高级全程记录之13 (京东项目一)
学习目的 本期将会学习一个京东项目,综合知识非常多.首先我们要先了解一下这个项目的知识. 1.京东项目(一) 项目名称:京东网 项目描述:京东首页公共部分的头部和尾部制作,京东首页中间部分. 项目背景 ...
- 前端学习从入门到高级全程记录之39 (PHP基础Ⅱ)
学习目标 本期我们将继续学习php的相关知识,在了解了上一期的PHP的一些基本语法和方法,接下来就是实战做做小例子.如果还不会配置php的运行环境的,可以参考我的上一期内容.如果上一期内容看不懂的,可 ...
最新文章
- python如何强制结束主线程_强制结束线程
- jsp springmvc 视图解析器_springMVC配置jsp/html视图解析器
- Kanas.net Framework 入门介绍
- 1007 素数对猜想(C语言)
- coco 数据集_Tensorflow对COCO目标检测数据预处理
- Ubuntu14.04编译Opencv3.1错误:下载ippicv,解决方案
- 第二次项目冲刺(Beta阶段)--第五天
- 信息论基础(学习笔记整理)
- cad自动填写页码lisp_图框文件名称自动填写 - AutoLISP/Visual LISP 编程技术 - CAD论坛 - 明经CAD社区 - Powered by Discuz!...
- 复制EXCEL单元格的值到SpreadJs单元格中,会多加一个可见的空格和一个不可见的0宽度空格的解决方法(ie11)
- 【论文解读】目标检测之RFBnet模型
- iOS开发学习之大牛们的博客
- CSS为字体添加过度色
- 《工程伦理与学术道德》之《导论》
- word恢复到安装时的状态?
- replaceAll()如何同时替换多个不同的字符串(或多个符号)
- 读彼得林奇的成功投资有感
- 通过各种实践活动 培养学生道德品质
- JavaScript获取时间戳的坑
- tensorflow入门教程(三十五)facenet源码分析之MTCNN--人脸检测及关键点检测