第12章 Ajax

  • XHR
  • 封装 XHR
    • 回调函数
    • 兼容性问题
  • jQuery 的快捷方法
  • axios
  • 跨域
    • JSONP
    • CORS
  • XHR 2.0
    • FormData
    • 文件上传
  • 客户端模板引擎

案例

  • 增删改查

学习目标

  • Ajax

    • 能够概述什么是Ajax
    • 能够理解传统模式交互和Ajax模式交互的异同
  • 原生 XHR

    • 能够掌握使用原生 XHR 发起 GET 请求
    • 能够掌握使用原生 XHR 发起 POST 请求
    • 能够理解 GET 请求与 POST 请求的区别
    • 能够理解同步请求和异步请求的差异
    • 能够理解什么是 GET 缓存
    • 能够掌握让 GET 缓存失效的解决方法
  • JSON
    • 能够理解并概述什么是 JSON
    • 能够掌握将 JSON 格式字符串转换为 JavaScript 对象
    • 能够掌握将 JavaScript 对象转换为 JSON 格式字符串
  • 客户端模板引擎
    • 能够理解模板引擎的本质作用
    • 能够理解模板引擎的实现原理
    • 能够掌握使用模板引擎将请求响应数据渲染到页面中
  • 封装 Ajax

    • 能够掌握 GET 请求方法的封装
    • 能够掌握 POST 请求方法的封装
    • 能够掌握 GET+POST 请求方法的封装
    • 能够理解在异步操作中回调函数的意义
  • jQuery 中的 Ajax

    • 能够掌握 $.ajax 的使用
    • 能够掌握 $.get 的使用
    • 能够掌握 $.post 的使用
  • XHR 2.0
    • 能够掌握 FormData 对象的使用
    • 能够掌握使用 XHR 2.0 异步上传文件
    • 能够掌握使用 XHR 2.0 实现文件上传进度条
  • 跨域
    • 能够理解什么是 Ajax 跨域
    • 能够理解什么是同源策略
    • 能够掌握使用 CORS 的方式进行跨域操作
    • 能够掌握使用 JSONP 的方式进行跨域操作
    • 能够理解 JSONP 跨域操作原理
    • 能够掌握 jQuery 中的 ajax 通过 JSONP 进行跨域操作

概述

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

browser-server

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

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

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

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

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

能力不够 API 凑。

快速上手

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

  1. // 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. }

readyState

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

readyState 状态描述 说明
0 UNSENT 代理(XHR)被创建,但尚未调用 open() 方法。
1 OPENED open() 方法已经被调用,建立了连接。
2 HEADERS_RECEIVED send() 方法已经被调用,并且已经可以获取状态行和响应头。
3 LOADING 响应体下载中, responseText 属性可能已经包含部分数据。
4 DONE 响应体下载完成,可以直接使用 responseText
  1. var xhr = new XMLHttpRequest()
  2. // 代理(XHR)被创建,但尚未调用 open() 方法。
  3. console.log(xhr.readyState)
  4. // => 0
  5. xhr.open('GET', './time.php')
  6. // open() 方法已经被调用,建立了连接。
  7. console.log(xhr.readyState)
  8. // => 1
  9. xhr.send(null)
  10. xhr.onreadystatechange = function () {
  11. console.log(this.readyState)
  12. // send() 方法已经被调用,并且已经可以获取状态行和响应头。
  13. // => 2
  14. // 响应体下载中, responseText 属性可能已经包含部分数据。
  15. // => 3
  16. // 响应体下载完成,可以直接使用 responseText。
  17. // => 4
  18. }

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

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

遵循 HTTP

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

  1. // 设置请求报文的请求行
  2. xhr.open('GET', './time.php')
  3. // 设置请求头
  4. xhr.setRequestHeader('Accept', 'text/plain')
  5. // 设置请求体
  6. xhr.send(null)
  7. xhr.onreadystatechange = function () {
  8. if (this.readyState === 4) {
  9. // 获取响应状态码
  10. console.log(this.status)
  11. // 获取响应状态描述
  12. console.log(this.statusText)
  13. // 获取响应头信息
  14. console.log(this.getResponseHeader('Content-Type')) // 指定响应头
  15. console.log(this.getAllResponseHeader()) // 全部响应头
  16. // 获取响应体
  17. console.log(this.responseText) // 文本形式
  18. console.log(this.responseXML) // XML 形式,了解即可不用了
  19. }
  20. }

参考链接:

具体用法

GET 请求

通常在一次 GET 请求过程中,参数传递都是通过 URL 地址中的 ? 参数传递。

  1. var xhr = new XMLHttpRequest()
  2. // GET 请求传递参数通常使用的是问号传参
  3. // 这里可以在请求地址后面加上参数,从而传递数据到服务端
  4. xhr.open('GET', './delete.php?id=1')
  5. // 一般在 GET 请求时无需设置响应体,可以传 null 或者干脆不传
  6. xhr.send(null)
  7. xhr.onreadystatechange = function () {
  8. if (this.readyState === 4) {
  9. console.log(this.responseText)
  10. }
  11. }
  12. // 一般情况下 URL 传递的都是参数性质的数据,而 POST 一般都是业务数据

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. }

同步与异步

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

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

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

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

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

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

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

演示同步异步差异。

一定在发送请求 send() 之前注册 readystatechange(不管同步或者异步)

  • 为了让这个事件可以更加可靠(一定触发),一定是先注册

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

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

响应数据格式

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

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

XML

一种数据描述手段

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

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

JSON

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

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

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

处理响应数据渲染

模板引擎:

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

兼容方案

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

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

封装

AJAX 请求封装

jQuery.ajax

跨域

相关概念

解决方案

JSONP

CORS

XHR 2.0

暂作了解,无需着重看待

onload / onprogress

FormData

参考链接