AJAX 全称为 Asynchronous JavaScript And XML,就是异步的 JS 和 XML。
通过 AJAX 可以在浏览器中向服务器发送异步请求,最大的优势:无刷新获取数据。
AJAX 不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式。
概念:XML是可扩展标记语言,被设计用来传输和存储数据。
XML 与 HTML:HTML 中都是预定义标签,而 XML 中没有预定义标签, 全都是自定义标签,用来表示一些数据。
孙悟空
18
男
{"name":"孙悟空","age":18,"gender":"男"}
(1)可以无需刷新页面而与服务器端进行通信。
(2)允许你根据用户事件来更新部分页面内容。
(1)没有浏览历史,不能回退.
(2)存在跨域问题 ( 同源 ) 。
(3)SEO 不友好(爬虫)。
XMLHttpRequest,AJAX 的所有操作都是通过该对象进行的。
使用步骤:
(1)创建 XMLHttpRequest 对象。
(2)设置请求信息(可以设置请求头,看具体需要)。
(3)发送请求。
(4)接收响应。
示例:
// 1. 导入 express 模块
const express = require('express');
// 2. 创建 web 服务器
const app = express();
// 暴露静态资源
app.use(express.static(__dirname + '/src'))
// 3. 调用 app.listen(端口号,启动成功后的回调函数) 启动服务器
app.listen(8080, (err) => {
if (!err) {
console.log(`http://127.0.0.1:8080/01_4种状态.html`);
}
})
xhr 的 4 种状态:
0:实例出来的那一刻状态就是0,初始状态。
1:open 已经调用了,但是send 还没有调用,此时可以修改请求头内容。
2:send 已经调用了,已经无法修改请求头。
3:已经回来一部分数据,小数据会在此阶段一次性接收完毕,较大的数据有待进一步接收,响应头回来了。
4:数据全部接收完毕。
(1)在 server.js 中增加 GET 监听。
监听 GET 请求:
(1)参数1:客户端请求的 URL 地址。
(2)参数2:请求对应的处理函数
① req:请求对象(包含了于请求相关的属性与方法)。
② res:响应对象(包含了与响应相关的属性和方法)。
如果 res.send() 中发送的是 json 格式的数据,可以在 xxx.html ==> xhr.open() 后面增加【xhr.responseType = 'json'】 来自动解析 json 格式数据。
app.get('/test_get', (req, res) => {
console.log('hello, get!');
// 接收 query 参数
console.log(req.query);
res.send(`--- hello, get! ---`)
})
(2)在 xxx.html 中设置 GET 请求。
GET 没有请求体,可以携带 query 和 params 参数。
query:http://127.0.0.1:8080/test_get?name=zs&age=20
params:http://127.0.0.1:8080/test_get/zs/20
// GET 请求
xhr.open('GET', 'http://127.0.0.1:8080/test_get?name=zs&age=20'); // query 参数
xhr.open('GET', 'http://127.0.0.1:8080/test_get/zs/20'); // params 参数
(1)在 server.js 中增加 POST 监听。
监听 POST 请求:
(1)参数1:客户端请求的 URL 地址。
(2)参数2:请求对应的处理函数
① req:请求对象(包含了于请求相关的属性与方法)。
② res:响应对象(包含了与响应相关的属性和方法)。
app.post('/test_post', (req, res) => {
console.log('hello, post!');
// 接收 body 参数
console.log(req.body);
res.send(`--- hello, post! ---`)
})
(2)在 xxx.html 中设置 POST 请求。
POST 有请求体,一共有三种参数:query、 params、body(一般用body)。
body 有两种编码格式:urlencoded、json。
// POST 请求
xhr.open('POST', 'http://127.0.0.1:8080/test_post');
(1)在 server.js 中使用中间件(中间件是 nodejs 里的东东)。
// 使用中间件解析 urlencoded 编码形式的请求体参数
app.use(express.urlencoded({ extended: true }));
// 使用中间件解析 json 编码形式的请求体参数
app.use(express.json());
(2)在 xxx.html 中追加请求头
// 追加响应头,用于标识携带请求体参数的编码形式 -- urlencoded
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
// 追加响应头,用于标识携带请求体参数的编码形式 -- json
xhr.setRequestHeader('Content-type', 'application/json')
server.js
// 1. 导入 express 模块
const express = require('express');
// 2. 创建 web 服务器
const app = express();
app.use(express.static(__dirname + '/src'))
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.get('/test_get', (req, res) => {
console.log('hello, get!');
console.log(req.query);
res.send(`--- hello, get! ---`)
})
app.post('/test_post', (req, res) => {
console.log('hello, post!');
console.log(req.body);
res.send(`--- hello, post! ---`)
})
// 3. 调用 app.listen(端口号,启动成功后的回调函数) 启动服务器
app.listen(8080, (err) => {
if (!err) {
console.log(`http://127.0.0.1:8080/01_4种状态.html`);
}
})
xxx.html
问题:IE 浏览器存在强缓存机制,ajax 只会发送的第一次请求,剩余多次请求不会在发送给浏览器而是直接加载缓存中的数据。(简单的说,就是当我第二次发送请求时,浏览器发现地址没有变,并没有给服务器发送请求,直接显示第一次请求的信息,这就导致在相同地址下,我发送了不同的数据但是没有进行更改。)
解决方案:浏览器的缓存是根据 url 地址来记录的,所以我们只需要修改 url 地址即可避免缓存问题。
// 增加时间戳保证 URL 地址不同
xhr.open("get","/testAJAX?t="+Date.now());
// 配置出错的回调
xhr.onerror = () => {
console.log('当前网络不稳定,请稍后重试');
}
// 设定超时时间 ( 2000 就是 2ms )
xhr.timeout = 2000
// 设置超时的回调
xhr.ontimeout = () => {
console.log('网速不给力,请稍后重试');
}
// 语法
xhr.abort()
思考:取消请求之前这个请求有没有到达服务器?
答:存在两种可能:① 还没有发送到服务器就被取消了。② 已经发送到服务器并且返回了信息,但是不要了。
基本思想:在创建下一个 xhr 实例之前先判断一下目前的 xhr 是不是正在加载中。如果是,则取消之前的请求,创建新的 xhr 实例。
原因是浏览器为了安全,而采用的同源策略 ( Same origin policy )。
(1)同源策略是由 Netscape 提出的一个著名的安全策略,现在所有支持 JavaScript 的浏览器都会使用这个策略。
(2)Web 是构建在同源策略基础智商的,浏览器只是针对同源策略的一种实现。
(3)同源:协议、域名(IP)、端口必须要完全相同,即协议、域名(IP)、端口都相同,才算是在同一个域里。
请求地址 | 形式 | 结果 |
http://study.cn/test/a.html | 协议、域名、端口都相同 | 成功 |
http://a.study.cn/test/a.html | 域名不同 | 失败 |
http://study.cn:8080/test/a.html | 端口不同 | 失败 |
https://study.cn/test/a.html | 协议不同 | 失败 |
(1)Cookie 不能读取。
(2)DOM 无法获得。
(3)AJAX 请求不能获取数据。【注意:是不能获取数据,并不是不能发送请求】
JSONP(JSON with Padding),是一个非官方的跨域解决方案,纯粹凭借程序员的聪明 才智开发出来,只支持 get 请求。
在网页有一些标签天生具有跨域能力,比如:img link iframe script。
JSONP 就是利用 script 标签的跨域能力来发送请求的。
// xxx.html
// server.js
app.get('/test_jsonp', (req, res) => {
console.log('收到请求!');
const { callback } = req.query
const person = { name: 'zs', age: 20 }
res.send(`${callback}(${JSON.stringify(person)})`)
})
注意点:
模板字符串里的内容要是字符串,直接传 person 对象默认使用 toString() ,最终得到结果是 demo([Object Object]),这明显是错误的。要使得对象能够变成字符串并且还能获取到其中的内容,就需要用到JSON.stringify()。
res.send(`${callback}(${JSON.stringify(person)})`)
(1)jsonp 是如何解决跨域问题的?
答:jsonp 解决跨域问题的原理就是绕开 xhr,借助 script 标签的跨域能力来发送请求。
(2)json 和 jsonp 有关系吗?
答:有一定的关系,但是两者是两回事。json 是一种存储和交互数据的格式,jsonp 是后端解决跨域问题的一种方式。
关系:jsonp 后端返回数据的时候必须得是字符串的形式。
(3)jsonp 解决跨域问题的时候也是用到了 xhr,对吗?
答:不对,jsonp 绕开了 xhr。
(4)jsonp 解决跨域存在什么优缺点?
答:JSONP的优缺点_SalmonellaVaccine的博客-CSDN博客
CORS(Cross-Origin Resource Sharing),跨域资源共享。CORS 是官方的跨域解决方案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持 get 和 post 请求。跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些 源站通过浏览器有权限访问哪些资源。
CORS 是通过设置响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。
3.3.1 简单请求 ( GET / POST )
app.get('/test_cors_get', (req, res) => {
// res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500');
res.setHeader('Access-Control-Allow-Origin', '*');
// Access-Control-Expose-Headers 列出了哪些首部可以作为响应的一部分暴露给外部
res.setHeader('Access-Control-Expose-Headers', '*');
res.send('hello_cors_get!');
})
3.3.2 复杂请求 ( PUT / DELETE / ... )
复杂请求向服务器发送了两次请求:预检请求、实际请求。
若不处理预检请求,则第一次请求失败,依旧会报跨域错误:
处理预检请求后:
预检请求:
在 CORS 中,可以使用 OPTIONS 方法发起一个预检请求,以检测实际请求是否可以被服务器所接受。预检请求报文中的 Access-Control-Request-Method 首部字段告知服务器实际请求所使用的 HTTP 方法;Access-Control-Request-Headers 首部字段告知服务器实际请求所携带的自定义首部字段。服务器基于从预检请求获得的信息来判断,是否接受接下来的实际请求。
参考:OPTIONS - HTTP | MDN
app.options('/test_cors_put', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Expose-Headers', '*');
// Access-Control-Allow-Methods 在对 preflight request.(预检请求)的应答中
// 明确了客户端所要访问的资源允许使用的方法或方法列表
res.setHeader('Access-Control-Allow-Methods', '*');
res.send();
})
app.put('/test_cors_put', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Expose-Headers', '*');
res.send('hello_cors_put!');
})
(1)先下载 cors,我用的 npm。
npm install cors
(2)按照之前的基本结构正常的发送请求信息即可。
// server.js
// 1. 导入 express 模块
const express = require('express');
// 1. 导入 cors 模块
const cors = require('cors');
// 2. 创建 web 服务器
const app = express();
app.use(express.static(__dirname + '/src'))
// 3. 全局生效的中间件
app.use(cors())
// 4. 匹配需要的请求
app.get('/test_cors_get', (req, res) => {
res.send('hello_cors_get!');
})
app.put('/test_cors_put', (req, res) => {
res.send('hello_cors_put!');
})