2022-04-06
代码
0
请注意,本文编写于 1177 天前,最后修改于 517 天前,其中某些信息可能已经过时。

目录

原生Ajax
简介
Ajax
XML
JSON
Ajax优缺点
启动express服务
安装nodemon
Ajax案例
案例准备
基本操作
发送POST请求
服务端响应JSON
IE缓存问题解决
请求超时与网络异常
取消请求
请求重复发送问题
jQuery Ajax
GET POST
通用方法
axios Ajax
GET
POST
AJAX
fetch函数
跨域
同源策略
JSONP
原生
jQuery发送JSONP
CORS

原生Ajax

简介

Ajax

Ajax 全称为 Asynchronous JavaScript And XML ,就是异步的 JSXML

通过 Ajax 可以在浏览器中向服务器发送异步请求,最大的优势:无刷新获取数据

Ajax 不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式。

XML

XML 是可扩展标记语言,被设计用来传输和存储数据。

XMLHTML 类似,不同的是 HTML 中都是预定义标签,而 XML 中没有预定义标签,全都是自定义标签,用来表示一些数据。

xml
<student> <name>Tom</name> <age>20</age> <gender></gender> </student>

JSON

JavaScript Object NotationJavaScript 对象表示法)

JSON 是存储和交换文本信息的语法,类似 XML,比 XML 更小、更快,更易解析。

json
{"student":{"name":"Tom","age":"20","gender":"男"}}

Ajax优缺点

优点:

  1. 可以无需刷新页面而与服务器端进行通信。
  2. 允许根据用户事件(鼠标事件、键盘事件、表单事件、文档事件等)来更新部分页面内容。

缺点:

  1. 没有浏览历史,不能回退
  2. 存在跨域问题(同源)
  3. SEO 不友好

启动express服务

执行npm init --yes

bash
npm i express
js
// 引入express const express = require('express') // 创建应用对象 const app = express() // 创建路由规则 // request 是对请求报文的封装 // response 是对响应报文的封装 app.get('/', (request, response)=>{ response.send('Hello Express') }) // 监听端口启动服务 app.listen(8001, ()=>{ console.log('服务已启动,端口号:8001') })

安装nodemon

bash
npm install -g nodemon

Ajax案例

案例准备

html页面

html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <style type="text/css"> #result { margin-top: 10px; width: 200px; height: 100px; border: 2px solid #9900BB; } </style> </head> <body> <button type="button">点击发送请求</button> <div id="result"></div> </body> </html>

server.js

js
// 引入express const express = require('express') // 创建应用对象 const app = express() // 创建路由规则 // request 是对请求报文的封装 // response 是对响应报文的封装 app.get('/server', (request, response)=>{ // 设置响应头,设置允许跨域 response.setHeader('Access-Control-Allow-Origin', '*') // 设置响应体 response.send('Hello Ajax') }) // 监听端口启动服务 app.listen(8001, ()=>{ console.log('服务已启动,端口号:8001') })

image-20220406173535644

基本操作

html
<script type="text/javascript"> // 获取元素 const btn = document.getElementsByTagName('button')[0] const result = document.getElementById('result') // 绑定事件 btn.onclick = function() { // 进行Ajax操作 // 1. 创建对象 const xhr = new XMLHttpRequest() // 2. 初始化 设置请求方法和url xhr.open('GET', 'http://localhost:8001/server') // 3. 发送 xhr.send() // 4. 事件绑定 处理服务端返回的结果 xhr.onreadystatechange = function() { // 判断服务端返回了所有的结果(5个状态:0 1 2 3 4) if(xhr.readyState === 4) { // 判断响应状态码 // 2xx 成功 if(xhr.status >= 200 && xhr.status < 300) { // 处理结果 行 头 空行 体 // console.log(xhr.status) // 状态码 // console.log(xhr.statusText) // 状态字符串 // console.log(xhr.getAllResponseHeaders()) // 所有响应头 // console.log(xhr.response) // 响应体 result.innerHTML = xhr.response } } } } </script>

GIF 2022-4-6 18-01-39

为GET请求设置参数:xhr.open('GET', 'http://localhost:8001/server?a=1&b=2&c=3')

HTTP状态码

分类分类描述
1**信息,服务器收到请求,需要请求者继续执行操作
2**成功,操作被成功接收并处理
3**重定向,需要进一步的操作以完成请求
4**客户端错误,请求包含语法错误或无法完成请求
5**服务器错误,服务器在处理请求的过程中发生了错误

发送POST请求

需求:鼠标移入div框,发送POST请求,并在框中展示响应体

html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Ajax POST请求</title> <style type="text/css"> #result { width: 200px; height: 100px; border: 2px solid #9900BB; } </style> </head> <body> <div id="result"></div> <script type="text/javascript"> const result = document.getElementById('result') result.addEventListener('mouseover', function(){ // 1. 创建对象 const xhr = new XMLHttpRequest() // 2. 初始化 设置请求类型和url xhr.open('POST', 'http://localhost:8001/server') // 3. 发送 xhr.send() // 4. 事件绑定 xhr.onreadystatechange = function() { if(xhr.readyState === 4) { if(xhr.status >= 200 && xhr.status < 300) { // 处理服务端返回的结果 result.innerHTML = xhr.response } } } }) </script> </body> </html>

服务器端同时需要添加post方法:

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

GIF 2022-4-6 18-30-04

为POST请求设置参数:a=1&b=2&c=3'),可以写任意类型任意格式,只要后端能够处理即可

设置请求头:

在send后写语句xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')

当设置自定义请求头时,须在server.js中添加response.setHeader('Access-Control-Allow-Headers', '*'),并将post方法改为all(即可以接收任意类型的请求)

服务端响应JSON

需求:按下键盘按键,向服务端发送请求,服务端返回结果

服务器端设置

js
app.all('/json-server', (request, response)=>{ // 设置响应头,设置允许跨域 response.setHeader('Access-Control-Allow-Origin', '*') // 设置响应头,设置允许自定义请求头 response.setHeader('Access-Control-Allow-Headers', '*') // 响应一个数据 const data = { name: 'abc', age: 12 } // 对对象进行字符串转换 let str = JSON.stringify(data) // 设置响应体 response.send(str) // send()只能接收str或buffer })

手动转换数据

js
const result = document.getElementById('result') // 绑定键盘按下事件 window.onkeydown = function () { // 1. 创建对象 const xhr = new XMLHttpRequest() // 2. 初始化 设置请求方法和url xhr.open('GET', 'http://localhost:8001/json-server') // 3. 发送 xhr.send() // 4. 事件绑定 处理服务端返回的结果 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { // 手动对数据进行转换 let data = JSON.parse(xhr.response) console.log(data) result.innerHTML = data.name } } } }

自动转换数据

js
const result = document.getElementById('result') // 绑定键盘按下事件 window.onkeydown = function () { // 1. 创建对象 const xhr = new XMLHttpRequest() xhr.responseType = 'json' // 2. 初始化 设置请求方法和url xhr.open('GET', 'http://localhost:8001/json-server') // 3. 发送 xhr.send() // 4. 事件绑定 处理服务端返回的结果 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { // 手动对数据进行转换 /* let data = JSON.parse(xhr.response) console.log(data) result.innerHTML = data.name */ // 自动转换 console.log(xhr.response) result.innerHTML = xhr.response.name } } } }

GIF 2022-4-6 20-12-20

IE缓存问题解决

使用时间戳来解决

js
xhr.open('GET', 'http://localhost:8001/ie?t=' + Date.now())

请求超时与网络异常

server.js设置延时响应

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

Ajax处理请求超时和网络异常

js
// 超时设置 2s xhr.timeout = 2000 // 超时回调 xhr.ontimeout = function() { alert('请求超时,请稍后重试!') } // 网络异常回调 xhr.onerror = function() { alert('网络好像开小差了') }

取消请求

html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>取消请求</title> </head> <body> <button type="button">点击发送</button> <button type="button">点击取消</button> <script type="text/javascript"> const btns = document.querySelectorAll('button') let xhr = null btns[0].onclick = function() { xhr = new XMLHttpRequest() xhr.open('GET', 'http://localhost:8001/time') xhr.send() } btns[1].onclick = function() { xhr.abort() } </script> </body> </html>

GIF 2022-4-9 11-19-56.gif

请求重复发送问题

频繁点击按钮时始终会发送请求,可能导致服务器压力较大等

js
const btns = document.querySelectorAll('button') let xhr = null // 标识变量 let isSending = false // 是否正在发送ajax请求 btns[0].onclick = function() { if(isSending) xhr.abort() xhr = new XMLHttpRequest() isSending = true xhr.open('GET', 'http://localhost:8001/time') xhr.send() xhr.onreadystatechange = function() { if(xhr.readyState === 4) { isSending = false } } }

GIF 2022-4-9 11-47-17.gif

jQuery Ajax

GET POST

server.js

js
// jQuery服务 app.all('/jquery-server', (request, response)=>{ response.setHeader('Access-Control-Allow-Origin', '*') const data = { name: 'abc', age: 12 } // 对对象进行字符串转换 let str = JSON.stringify(data) // 设置响应体 response.send(str) // send()只能接收str或buffer })

client.html

html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>jQuery 发送 AJAX 请求</title> <link crossorigin="anonymous" href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> <script crossorigin="anonymous" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.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> <script> $('button').eq(0).click(function() { $.get('http://localhost:8001/jquery-server', {a: 100, b: 200}, function(data) { console.log(data) }, 'json') }) $('button').eq(1).click(function() { $.post('http://localhost:8001/jquery-server', {a: 100, b: 200}, function(data) { console.log(data) }) }) </script> </body> </html>

GIF 2022-4-9 14-55-49.gif

通用方法

js
$('button').eq(2).click(function() { $.ajax({ // url url: 'http://localhost:8001/jquery-server', // 参数 data: {a:100, b: 200}, // 请求类型 type: 'GET', // 相应体结果 dataType: 'json', // 成功的回调 success: function(data) { console.log(data) }, // 超时时间 timeout: 2000, // 失败的回调 error: function() { console.log('error') }, // 头信息 headers: {c: 300, d: 400} }) })

更多使用方法

axios Ajax

配置url根路径

js
axios.defaults.baseURL = 'http://localhost:8001'

GET

axios.get(url[, config])参数为url和配置信息

js
btns[0].onclick = function() { // GET 请求 axios.get('http://localhost:8001/axios-server', { // url参数 params: { id: 100, vip: 7 }, // 请求头信息 headers: { name: 'abc', age: 18 } }).then(value => { console.log(value) }) }

image-20220410081438589.png

POST

axios.post(url[, data[, config]])参数为url、请求体和配置信息

js
btns[1].onclick = function() { // POST请求 axios.post('http://localhost:8001/axios-server', { user: 'admin', password: 'admin' }, { // url参数 params: { id: 300, vip: 11 }, // 请求头信息 headers: { name: 'abc', age: 18 } }) }

image-20220410082125026.png

AJAX

js
btns[2].onclick = function () { axios({ // 请求方法 method: 'POST', // url url: '/axios-server', // url参数 params: { vip: 13, level: 2 }, // 头信息 headers: { a: 241, b: 21 }, // 请求体参数 data: { user: 'admin', password: '123456' } }).then(response => { console.log(response) // 响应状态码 console.log(response.status) // 响应状态字符串 console.log(response.statusText) // 响应头信息 console.log(response.headers) // 响应体 console.log(response.data) }) }

image-20220410083353037.png

fetch函数

js
btn.onclick = function() { fetch('http://localhost:8001/fetch-server?vip=10', { // 请求方法 method: 'POST', // 请求头 headers: { name: 'morales', naa: 'abc' }, // 请求体 body: 'username=admin&password=123456' }).then(response => { return response.text() // return response.json() }).then(response => { console.log(response) }) }

跨域

同源策略

同源策略(Same-Origin Policy)最早由 Netscape 公司提出,是浏览器的一种安全策略。

同源: 协议、域名、端口号 必须完全相同。

违背同源策略就是跨域。Ajax默认遵循同源策略。

示例:

server.js

js
const express = require('express') const app = express() app.get('/home', (request, response) => { // 响应一个页面 response.sendFile(__dirname + '/index.html') }) app.get('/data', (request, response) => { response.send('用户数据') }) app.listen(8001, () => { console.log('服务已启动') })

index.html

HTML
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>同源策略</title> </head> <body> <button type="button">获取用户数据</button> <script type="text/javascript"> const btn = document.querySelector('button') btn.onclick = function() { const x = new XMLHttpRequest() // 这里满足同源策略,url可以简写 x.open('GET', '/data') x.send() x.onreadystatechange = function() { if(x.readyState === 4) { if(x.status >= 200 && x.status < 300) { console.log(x.response) } } } } </script> </body> </html>

JSONP

JSONP(JSON with Padding),是一个非官方的跨域解决方案,只支持get请求

在网页有一些标签天生具有跨域能力,比如:imglinkiframescriptJSONP 就是利用 script 标签的跨域能力来发送请求的。

原生

js
// jsonp服务 app.all('/jsonp-server', (request, response) => { const data = { name: 'absfe' } let str = JSON.stringify(data) response.end(`handle(${str})`) })
html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>JSONP解决跨域</title> <style type="text/css"> #result { width: 300px; height: 100px; border: #225588 1px solid; background-color: aliceblue; } </style> </head> <body> <div id="result"></div> <script type="text/javascript"> function handle(data) { const result = document.getElementById('result') result.innerHTML = data.name } </script> <script src="http://localhost:8001/jsonp-server" type="text/javascript" charset="utf-8"></script> </body> </html>

JSONP案例:检测用户名已存在

server.js

js
// 用户名检测是否存在 app.all('/check-username', (request, response) => { const data = { exist: 1, msg: '用户名已经存在' } // 将数据转化为字符串 let str = JSON.stringify(data) // 返回结果 response.end(`handle(${str})`) })

username.html

html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>案例</title> </head> <body> 用户名:<input type="text" name="username" id="username" placeholder="请输入用户名" /> <p></p> <script type="text/javascript"> const input = document.getElementById('username') const p = document.querySelector('p') // 声明 handle 函数 function handle(data) { // 修改 input 框 input.style.border = "solid 1px #f00"; // 修改 p 标签的提示文本 p.innerHTML = data.msg; } // 绑定事件 input.onblur = function() { // 获取用户的输入值 let username = this.value; //向服务器端发送请求 检测用户名是否存在 // 1. 创建 script 标签 const script = document.createElement('script'); // 2. 设置标签的 src 属性 script.src = 'http://localhost:8001/check-username'; // 3. 将 script 插入到文档中 document.body.appendChild(script); } </script> </body> </html>

GIF 2022-4-10 11-26-08.gif

jQuery发送JSONP

server.js

js
// jquery-jsonp服务 app.all('/jquery-jsonp', (request, response) => { const data = { name: 'ati', location: ['a', 'b', 'c'] } // 接收 callback 参数 let cb = request.query.callback; // 将数据转化为字符串 let str = JSON.stringify(data) // 返回结果 response.end(`${cb}(${str})`) })

index.html

html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>jQuery-JSONP</title> <style> #result { width: 300px; height: 100px; border: solid 1px #089; } </style> <script crossorigin="anonymous" src='https://cdn.bootcss.com/jquery/3.5.0/jquery.min.js'></script> </head> <body> <button>点击发送 jsonp 请求</button> <div id="result"></div> <script> $('button').eq(0).click(function() { $.getJSON('http://localhost:8001/jquery-jsonp?callback=?', function(data) { $('#result').html(` 名称: ${data.name}<br> 位置: ${data.location} `) }); }); </script> </body> </html>

?callback=?是固定写法

GIF 2022-4-10 11-39-41.gif

CORS

CORSCross-Origin Resource Sharing),跨域资源共享。CORS 是官方的跨域解决方案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持 getpost 请求。跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。

CORS 是通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。

js
response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Headers", '*'); response.setHeader("Access-Control-Allow-Method", '*');

本文作者:Morales

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 License 许可协议。转载请注明出处!