Ajax简介

Asynchronous JavaScript And XML:在不网页无刷新跳转的情况下,向服务器(后端)发送HTTP请求并得到HTTP响应

Ajax就是一个异步无刷新请求,无需更新整个页面就异步加载一些数据,交互性更强。

Ajax优缺点

  1. 无需刷新页面即可与服务器端进行通信。
  2. 允许根据用户事件来更新部分页面内容。
  1. 无浏览历史,不能回退。
  2. 存在跨域问题(同源):如a.comb.com发送AJAX请求默认是不可行的。
  3. SEO(Search Engine Optimization)不友好。

如何使用Ajax?

  • 原生
  • JQuery
  • fetch函数
  • axios工具包

XML简介

可扩展标记语言(eXtensible Markup Language)。

与HTML的区别?

XML 被设计用来传输和存储数据,其焦点是数据的内容;HTML 被设计用来显示数据,其焦点是数据的外观。

(1)可扩展性方面:HTML不允许用户自行定义他们自己的标识或属性,而在XML中,用户能够根据需要自行定义新的标识及属性名,以便更好地从语义上修饰数据。

(2)结构性方面:HTML不支持深层的结构描述,XML的文件结构嵌套可以复杂到任意程度,能表示面向对象的等级层次。

(3)可校验性方面:HTML没有提供规范文件以支持应用软件对HTML文件进行结构校验,而XML文件可以包括一个语法描述,使应用程序可以对此文件进行结构校验。

AJAX的数据传输格式现在已经逐渐被JSON取代

HTTP相关问题

MDN 文档

HTTP 请求交互的基本过程
  1. 应用从浏览器端向服务器发送HTTP请求(请求报文)
  2. 后台服务器接收到请求后, 调度服务器应用处理请求, 向浏览器端返回HTTP响应(响应报文)
  3. 浏览器端接收到响应, 解析显示响应体/调用监视回调
  • HTTP请求报文
请求行    method URL 协议版本
如:POST /s?ie=utf-8 HTTP/1.1
---
请求头
Host: baidu.com
Cookie: BAIDUID=AD3B0FA706E; BIDUPSID=AD3B0FA706;
Content-Type: application/x-www-form-urlencoded # 或者application/json
User-Agent: chrome ...
...
---
请求体
username=tom&pwd=123 # or {"username": "tom", "pwd": 123}
  • HTTP响应报文
响应状态行    status statusText
如:200 OK
---
响应头
Content-Type: text/html?charset=utf-8
Content-Length: 2048
Content-encoding: gzip
...
响应体
# HTML代码/json文本/js/css/图片
  • POST请求体参数格式
Content-Type: application/x-www-form-urlencoded;charset=utf-8
# 用于键值对参数,参数的键值使用“=”连接,参数之间使用“&”连接
如:name=小小&age=11
---
Content-Type: application/json;charset=utf-8
# 用于json字符串参数
如:{"name": "小小","age":11}
---
Content-Type: multipart/form-data
# 用于文件上传请求

常见的响应状态码

200 OK 请求成功。一般用于GET 与POST 请求
201 Created 已创建。成功请求并创建了新的资源
401 Unauthorized 未授权/请求要求用户的身份认证
404 Not Found 服务器无法根据客户端的请求找到资源
500 Internal Server Error 服务器内部错误,无法完成请求

不同类型的请求及其作用

  1. GET: 从服务器端读取数据(查)
  2. POST: 向服务器端添加新数据 (增)
  3. PUT: 更新服务器端已经数据 (改)
  4. DELETE: 删除服务器端数据 (删)

API 的分类

  1. REST API: restful (Representational State Transfer (资源)表现层状态转化)

    (1) 发送请求进行CRUD 哪个操作由请求方式来决定
    (2) 同一个请求路径可以进行多个操作
    (3) 请求方式会用到GET/POST/PUT/DELETE

  2. 非REST API: restless

    (1) 请求方式不决定请求的CRUD 操作
    (2) 一个请求路径只对应一个操作
    (3) 一般只有GET/POST

http请求与ajax请求的区别

  1. ajax请求是一种特别的 http请求
  2. 对服务器端来说,,没有任何区别, 区别在浏览器端
  3. 浏览器端发请求:只有XHRfetch 发出的才是ajax 请求,其它所有的都是非ajax 请求
  4. 浏览器端接收到响应
    (1) 一般请求:浏览器一般会直接显示响应体数据, 也就是我们常说的刷新/跳转页面
    (2) ajax请求:浏览器不会对界面进行任何更新操作, 只是调用监视的回调函数传入响应相关数据

原生AJAX:使用 XHR

XHR(XMLHttpRequest)用于与服务器交互数据,是AJAX功能实现所依赖的对象,JQuery中的AJAX就是对XHR的封装。

XMLHttpRequest 对象提供了对 HTTP 协议的完全的访问,包括做出 POST 和 HEAD 请求以及普通的 GET 请求的能力。XMLHttpRequest 可以同步或异步地返回 Web 服务器的响应,并且能够以文本或者一个 DOM 文档的形式返回内容。

XHR接口强制要求每个请求都具备严格的HTTP语义–应用提供数据和URL,浏览器格式化请求并管理每个连接的完整生命周期,所以XHR仅仅允许应用自定义一些HTTP首部,但更多的首部是不能自己设定的,如:

Accept-Charset, Accept-Encoding, Access-Control-* Host, Upgrade, Connection, Referer, Origin Cookie, Sec-, Proxy-, 及其他首部

准备工作

个人觉得 node.js(执行.js文件的JavaScript代码)和 npm 有些类似 python 解释器和 pip 包管理工具的关系。

  • 安装node.js

    Node.js发布于2009年5月,是一个基于Chrome V8引擎的JavaScript【事件驱动脚本语言】运行环境,使用了一个事件驱动、非阻塞式I/O模型, 让JavaScript 运行在服务端的开发平台。

    简单的说 Node.js 就是运行在服务端的 JavaScript

    Node可以在不新增额外线程的情况下,依然可以对任务进行并发处理 —— Node.js是单线程的。它通过事件循环(event loop)来实现并发操作,对此,我们应该要充分利用这一点 —— 尽可能的避免阻塞操作,取而代之,多使用非阻塞操作。

    安装地址:http://nodejs.cn/

  • 第一个程序

    Node.js 应用组成部分:

    1. 引入 required 模块:使用 require 指令来载入 Node.js 模块。
    2. 创建服务器:服务器可以监听客户端的请求,类似于 Apache 、Nginx 等 HTTP 服务器。
    3. 接收请求与响应请求 :服务器很容易创建,客户端可以使用浏览器或终端发送 HTTP 请求,服务器接收请求后返回响应数据。

    在项目的根目录下创建一个叫 server.js 的文件,写入以下代码:

    var http = require('http');http.createServer(function (request, response) {// 发送 HTTP 头部// HTTP 状态值: 200 : OK// 内容类型: text/plainresponse.writeHead(200, {'Content-Type': 'text/plain'});// 发送响应数据 "Hello World"response.send('Hello World\n');
    }).listen(8888);// 终端打印如下信息
    console.log('Server running at http://127.0.0.1:8888/');
    

    以上代码我们完成了一个可以工作的 HTTP 服务器。使用 node 命令执行以上的代码:

    node server.js
    ## Server running at http://127.0.0.1:8888/
    
  • NPM

    NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种:

    • 允许用户从NPM服务器下载别人编写的第三方包到本地使用。
    • 允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。
    • 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。

    由于新版的nodejs已经集成了npm,所以之前npm也一并安装好了。同样可以通过输入 “npm -v” 来测试是否成功安装。

  • 安装express(web框架模块)

    Express中文网:https://www.expressjs.com.cn/

    nodeJS相关和npm使用参考:https://www.runoob.com/nodejs/nodejs-npm.html

    1. 初始化环境(在项目根目录下)
    npm init --yes
    
    1. 安装express模块(项目路径下)
    npm install express --save
    npm install express -g --save  # 全局
    
    1. 编写js代码
    // 1. 引入express
    const express = require('express');// 2. 创建应用对象
    const app = express();// 3. 创建路由规则
    // request 是对请求报文的封装
    // response 是对响应报文的封装
    app.get('/', (request, response) => {//  设置响应response.send("Hello Express");
    });// 4. 监听端口,启动服务
    app.listen(8000, () => {console.log("server is running on http://127.0.0.1:8000/");})
    
    1. 运行程序
  • 安装nodemon自动重启工具

    文件内容有修改自动重新启动服务:https://www.npmjs.com/package/nodemon

    安装

    npm install -g nodemon
    

    启动服务

    ndoemon server.js
    

注意点

  1. 使用XMLHttpRequest (XHR)对象可以与服务器交互, 也就是发送ajax 请求
  2. 前端可以获取到数据,而无需让整个的页面刷新。(使得Web 页面可以只更新页面的局部,而不影响用户的操作)

XHR文档:https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest。
AJAX 的所有操作都是通过该对象进行的

核心对象使用步骤

  • 创建XMLHttpRequest 对象

    var xhr = new XMLHttpRequest();
    
  • 设置请求信息(请求方法和url)

    // 请求方式
    xhr.open(method, url);
    //可以设置请求头,一般不设置
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');// 自定义请求头
    xhr.setRequestHeader('name', 'moonjay');
    // 自定义请求头需要在server.js加入 response.setHeader('Access-Control-Allow-Headers', '*');
    
  • 发送请求

    xhr.send(body) //get请求不传 body 参数,只有post请求使用
    
  • 接收响应(事件绑定,处理服务端返回的结果)

    //xhr.responseXML 接收 xml格式 的响应数据
    //xhr.responseText 接收 文本格式 的响应数据
    xhr.onreadystatechange = function(){// readyState 是 xhr对象中的属性, 表示状态 0 1 2 3 4if(xhr.readyState == 4 && xhr.status == 200){var text = xhr.responseText;console.log(text);}
    }
    

AJAX 请求状态

xhr.readyState 可以用来查看请求当前的状态
文档链接:https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/readyState

  • 0: 表示XMLHttpRequest 实例已经生成,但是open()方法还没有被调用
  • 1: 表示send()方法还没有被调用,仍然可以使用setRequestHeader(),设定HTTP请求的头信息
  • 2: 表示send()方法已经执行,并且头信息和状态码已经收到
  • 3: 表示正在接收服务器传来的body 部分的数据
  • 4: 表示服务器数据已经完全接收,或者本次接收已经失败了

示例

  • GET 请求(点击返回响应信息)

    文件结构(浏览器端–html文件;服务器端–js文件)

    server.js

    // 1. 引入express
    const express = require('express');// 2. 创建应用对象
    const app = express();// 3. 创建路由规则
    app.get('/server', (request, response) => {// 设置响应头 设置允许跨域response.setHeader('Access-Control-Allow-Origin', '*');// 设置响应体response.send("Hello Ajax");
    });// 4. 监听服务
    app.listen(8000, () => {console.log("server is running on http://127.0.0.1:8000/");})
    

    01Get.html

    <!DOCTYPE html>
    <html lang="en">
    <head><meta charset="UTF-8"><title>Ajax GET 请求</title><style>#result {width: 200px;height: 100px;border: solid 1px #90b;}</style>
    </head>
    <body><button>点击发送请求</button><div id="result"></div><script>//获取button元素const btn = document.getElementsByTagName('button')[0];const result = document.getElementById('result');//绑定事件btn.onclick = function(){// 1. 创建对象const xhr = new XMLHttpRequest();// 2. 初始化 设置请求方法和urlxhr.open('GET', 'http://127.0.0.1:8000/server')// 3. 发送, GET不发送body POST发送xhr.send();// 4. 事件绑定 处理服务端返回的结果xhr.onreadystatechange = function(){// readyState 是 xhr 对象中的属性, 表示状态 0 1 2 3 4//判断 (服务端返回了所有的结果)if(xhr.readyState === 4){//判断响应状态码 200  404  403 401 500if(xhr.status >= 200 && xhr.status < 300){// 处理结果 行 头 空行 体// 响应行console.log('状态码', xhr.status); // 状态码console.log('状态字符串', xhr.statusText); // 状态字符串console.log('所有响应头', xhr.getAllResponseHeaders()); // 所有响应头console.log('响应体', xhr.response); // 响应体//设置 result 的文本result.innerHTML=xhr.response;}else{}}} }</script>
    </body>
    </html>
    

    GET 请求设置请求参数

    xhr.open('GET', 'http://127.0.0.1:8000/server?a=100&b=200&c=300');
    

    可以在Headers中看到Query String Parameters多出三个参数

  • POST请求(鼠标放到div中,发post请求,将响应体放在div中呈现)

    server.js添加POST

    app.post('/server', (request, response) => {// 设置响应头, 设置允许跨域response.setHeader('Access-Control-Allow-Origin', '*');// 设置响应体response.send("Hello Ajax POST");
    });
    

    01Post.html

    <!DOCTYPE html>
    <html lang="en">
    <head><meta charset="UTF-8"><title>Ajax POST 请求</title><style>#result {width: 200px;height: 100px;border: solid 1px #903;}</style>
    </head>
    <body><div id="result"></div><script>// 获取元素对象const result = document.getElementById('result');// 绑定事件result.addEventListener("mouseover", function(){// 1. 创建对象const xhr = new XMLHttpRequest();// 2. 初始化 设置类型(请求方式)与urlxhr.open('POST', 'http://127.0.0.1:8000/server');// 3. 发送   设置请求参数(请求体),可以说任意类型的xhr.send('a=100&b=200&c=300');// 4. 事件绑定xhr.onreadystatechange = function(){// 判断if(xhr.readyState === 4){if(xhr.status >=200 && xhr.status < 300){// 处理服务端返回的结果result.innerHTML = xhr.response;}}}});</script>
    </body>
    </html>
    

JSON数据请求

server.js中设置响应头允许自定义请求头post改成all

// app.all() -- 可以get、post
app.all('/json-server', (request, response) => {// 设置响应头, 设置允许跨域response.setHeader('Access-Control-Allow-Origin', '*');// 设置响应头, 设置允许自定义头信息response.setHeader('Access-Control-Allow-Headers', '*');// 响应一个数据const data = {name: 'moonjay'};// 对 对象 进行 字符串 转换let str = JSON.stringify(data)// 设置响应体response.send(str);
});

01JSON.html:(按下键盘任意按键触发)

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>JSON</title><style>#result {width: 200px;height: 100px;border: solid 1px #89b;}</style>
</head>
<body><div id="result"></div><script>const result = document.getElementById('result');// 绑定键盘按下事件window.onkeydown = function(){// 发送请求const xhr = new XMLHttpRequest();// *2*.(自动转换) 设置响应体数据的类型(自动转换)xhr.responseType = 'json';// 初始化xhr.open('GET', 'http://127.0.0.1:8000/json-server');// 发送xhr.send();// 事件绑定xhr.onreadystatechange = function(){if(xhr.readyState === 4){if(xhr.status >= 200 && xhr.status < 300){console.log(xhr.response);// 1. 手动对数据转化 (字符串再转换成json)// let data = JSON.parse(xhr.response); //转换成json// result.innerHTML = data.name;// 2. 自动转换result.innerHTML = xhr.response.name; //已经自动变成json}}}}</script>
</body>
</html>

请求超时与网络异常

// 超时设置 (2秒)
xhr.timeout = 2000;
// 超时回调
xhr.ontimeout = function(){alert('网络超时,请稍后重试')
}
// 网络异常回调   在chrome’检查‘的Network选项卡可以设置网络环境--设为offline
xhr.onerror = function(){alert('网络异常,请稍后重试')
}
// 手动取消请求
xhr.abort()

server.js中设置:

app.get('/delay', (request, response) =>{response.setHeader('Access-Control-Allow-Origin', '*');setTimeout(() => {response.send('延时响应');}, 3000)
});

重复请求问题

用户可能重复向服务器发送相同请求,造成服务器端响应压力,如何解决?

创建一个只有一个按钮的html网页,html代码略,下为script脚本:

const btn = document.querySelector('button')
let x = null;
// 标识变量 是否正在发送AJAX请求
let isSending = false;
btn.onclick = function(){// 判断标识变量,如果正在发送,则取消该请求创建一个新的请求if(isSending) x.abort();x = new XMLHttpRequest();// 修改标识变量的值isSending = true;x.open("GET", "http://127.0.0.1:8000/delay");x.send();x.onreadystatechange = function(){if(x.readyState === 4){//修改标识变量isSending = false;}}
}

IE 缓存问题

问题:在一些浏览器中(IE),由于缓存机制的存在,ajax 只会发送的第一次请求,剩余多次请求不会在发送给浏览器而是直接加载缓存中的数据。

解决方式:浏览器的缓存是根据url 地址来记录的,所以我们只需要修改url 地址即可避免缓存问题。

xhr.open("get","/testAJAX?t="+Date.now());

API总结

  • XMLHttpRequest():创建 XHR 对象的构造函数
  • status:响应状态码值,如 200、404
  • statusText:响应状态文本,如 ’ok‘、‘not found’
  • readyState:标识请求状态的只读属性 0-1-2-3-4
  • onreadystatechange:绑定 readyState 改变的监听
  • responseType:指定响应数据类型,如果是 ‘json’,得到响应后自动解析响应
  • response:响应体数据,类型取决于 responseType 的指定
  • timeout:指定请求超时时间,默认为 0 代表没有限制
  • ontimeout:绑定超时的监听
  • onerror:绑定请求网络错误的监听
  • open():初始化一个请求,参数为:(method, url[, async])
  • send(data):发送请求
  • abort():中断请求 (发出到返回之间)
  • getResponseHeader(name):获取指定名称的响应头值
  • getAllResponseHeaders():获取所有响应头组成的字符串
  • setRequestHeader(name, value):设置请求头

JQuery中的AJAX

get 请求

$.get(url, [data], [callback], [type])

post 请求

$.post(url, [data], [callback], [type])
  • url:请求的URL 地址
  • data:请求携带的参数
  • callback:载入成功时回调函数
  • type:设置返回内容格式,xml, html, script, json, text, _default

通用方法

$.ajax({// urlurl: 'http://127.0.0.1:8000/jquery-server',// 参数data: {a:100, b:200},// 请求类型type: 'GET',// 响应体结果dataType: 'json',// 成功的回调success: function(data){console.log(data);},// 超时时间timeout: 2000,// 失败的回调error: function(){console.log('出错la~');},// 头信息headers: {c: 300,d: 400}
})

示例

JQuery_AJAX.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>JQuery发送Ajax请求</title><!--crossorigin="anonymous"是跨域匿名请求的意思,向css或js文件发送请求时不会携带当前站点的cookies--><link crossorigin="anonymous" href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap-theme.min.css" rel="stylesheet"><script crossorigin="anonymous" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.min.js"></script></head><body><div class="container"><h2 class="page-header">JQuery发送Ajax请求</h2><button class="btn btn-primary">GET</button><button class="btn btn-danger">POST</button><button class="btn btn-info">通用型方法Ajax</button></div></body><script>$('button').eq(0).click(function(){$.get('http://127.0.0.1:8000/jquery-server', {a:100, b:200}, function(data){console.log(data)}, 'json');  //响应体类型为json})$('button').eq(1).click(function(){$.post('http://127.0.0.1:8000/jquery-server', {a:100, b:200}, function(data){console.log(data)}); //响应体类型为字符串})$('button').eq(2).click(function(){$.ajax({// urlurl: 'http://127.0.0.1:8000/jquery-server',// 参数data: {a:100, b:200},// 请求类型type: 'GET',// 响应体结果--可选,默认为字符串dataType: 'json',// 成功的回调success: function(data){console.log(data);},// 超时时间timeout: 2000,// 失败的回调error: function(){console.log('出错la~');},// 头信息headers: {c: 300,d: 400}});})</script>
</html>

server.js加入以下代码:

// JQuery_AJAX.html文件用  all--可以响应get和post
app.all('/jquery-server', (request, response) => {// 设置响应头response.setHeader('Access-Control-Allow-Origin', '*');// 设置响应头, 设置允许自定义头信息,// 第3个按钮有头信息参数,不设置会出错response.setHeader('Access-Control-Allow-Headers', '*');// response.send("hello JQuery AJAX");// 响应体类型为jsonconst data = {name: "moonjay"};response.send(JSON.stringify(data))
});

axios发送AJAX请求

axios的Github链接:https://github.com/axios/axios

是一个基于promise的网络请求库,作用于node.js和浏览器中,它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生node.js http模块, 而在客户端 (浏览端) 则使用XMLHttpRequest。

axios本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范

主要特点