AJAX 技术
ajax, Asynchronous Javascript and XML,异步 javascript 和 XML 技术,是一种早期的用于客户端和服务端异步数据交互的技术
特点
在不刷新页面的情况下从服务器获取数据或提交表单,异步增量式更新页面内容
Ajax 通信与数据格式无关,数据格式并不一定是 XML
优点
提高用户体验
通过减少页面的重载和刷新,使得网站变得更加灵活和动态。
减轻服务器负载
通过使用 Ajax,可以有效减少服务器接收到的请求次数和需要响应的数据量(相比 SSR),从而减轻服务器的负担。
提高响应速度
使用 Ajax 可以异步获取数据并更新页面,从而提高响应速度。
增加交互性
通过使用 Ajax,可以使得页面变得更加动态和交互性。
问题
不利于 SEO
动态生成网站内容、依赖用户的交互,而 SEO 主要检测静态内容
安全性
在使用 Ajax 时,需要考虑数据安全性和网络安全性问题,并采取相应的措施加以防范。
XMLHttpRequest 对象
XMLHttpRequest
是一个内建的浏览器对象
使用
1.创建 XML 对象
1
| let xhr = new XMLHttpRequest();
|
2.xhr.open()初始化
1
| xhr.open(method, URL, [async, user, password]);
|
此方法指定请求的主要参数:
method
—— HTTP 方法。通常是 "GET"
或 "POST"
。
URL
—— 要请求的 URL,通常是一个字符串,也可以是 URL 对象。
async
—— 如果显式地设置为 false
,那么请求将会以同步的方式处理,我们稍后会讲到它。
user
,password
—— HTTP 基本身份验证(如果需要的话)的登录名和密码。
请注意,open
调用与其名称相反,不会建立连接。它仅配置请求,而网络活动仅以 send
调用开启。
3.xhr.send()发送请求
这个方法会建立连接,并将请求发送到服务器。可选参数 body
包含了 request body。
一些请求方法,像 GET
没有 request body。还有一些请求方法,像 POST
使用 body
将数据发送到服务器。
4.监听状态变化
onreadystatechange
一个回调函数,在每次状态发生变化时被调用。
- readyState 0:未初始化,XMLHttpRequest 对象已经创建,但未调用 open 方法。
- readyState 1:已打开,open 方法已经被调用,但 send 方法未被调用。
- readyState 2:已发送,send 方法已经被调用,请求已经被服务器接收。
- readyState 3:正在接收,服务器正在处理请求并返回数据。
- readyState 4:完成,服务器已经完成了数据传输。
注意还需要检测 HTTP response status,即 HTTP 响应码是否为 200
1 2 3 4 5
| xhr.onreadystatechange = () => { if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { console.log(JSON.parse(xhr.response)); } };
|
onLoad onError onProgress
例子
1.GET
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const xhr = new XMLHttpRequest(); xhr.open('GET', '/api/txt', true); xhr.send(); xhr.onreadystatechange = () => { if (xhr.status === 200) { const divDOM = document.createElement('div'); divDOM.innerHTML = xhr.responseText; document.body.appendChild(divDOM); console.log('success'); } else { console.log('failed'); } }
|
2.POST application/json 格式参数
1 2 3 4 5 6 7 8 9 10
| const xhr = new XMLHttpRequest(); xhr.open('POST', '/api/post'); xhr.send(JSON.stringify({ name: 'mmy' })) xhr.onload = () => { if (xhr.status === 200) { console.log(JSON.parse(xhr.response)); } }
|
3.POST application/x-www-form-urlencoded 格式参数
1 2 3 4 5 6 7 8
| const xhr = new XMLHttpRequest(); xhr.open('POST', '/api/post'); xhr.send('name=mmy') xhr.onload = () => { if (xhr.status === 200) { console.log(JSON.parse(xhr.response)); } }
|
4.中断请求
1 2 3 4
| xhr.abort(); xhr.addEventListener('abort', function (event) { console.log('我被中断了'); });
|
5.设置超时
1 2 3 4
| xhr.timeout = 3000; xhr.addEventListener('timeout', function (event) { console.log('响应超时'); });
|
6.监听进度
监听 progress 事件,通过 event.loaded 和 event.total 属性获取已上传数据量和总数据量,并计算上传进度,最后将进度显示在页面上
1 2 3
| xhr.addEventListener('progress', function (event) { document.querySelector('#progress').innerText = `${(event.loaded / event.total * 100).toFixed(2)}%`; });
|
Fetch API
使用
1
| let promise = fetch(url, [options])
|
url
—— 要访问的 URL。
options
—— 可选参数:method,header 等
- method
- headers
- body
- singal
- credentials
- …
第一阶段,当服务器发送了响应头(response header),fetch
返回的 promise
就使用内建的 Response class 对象来对响应头进行解析。在这个阶段,我们可以通过 status 检查响应头的 HTTP 状态码,来检查 HTTP 状态以确定请求是否成功,当前还没有响应体(response body)。
第二阶段,为了获取 response body,我们需要使用一个其他的方法调用。返回数据的格式有五种
1 2 3 4 5
| text() 将响应体解析为纯文本字符串并返回 json() 将响应体解析为JSON格式并返回一个Javascript对象 blob() 将响应体解析为二进制数据并返回一个Blob对象 arrayBuffer() 将响应体解析为二进制数据并返回一个ArrayBuffer对象 formData() 将响应体解析为FormData对象
|
例子
1.GET(默认)
1 2 3 4 5 6 7 8 9 10 11
|
fetch('http://localhost:5173/api/txt') .then(res => { console.log(res); return res.text() }) .then(data => { console.log(data); })
|
2.POST application/json
1 2 3 4 5 6 7 8 9 10 11 12
| fetch('/api/post', { method: 'POST', headers: { 'Content-type': 'application/json' }, body: JSON.stringify({ name: 'mmy', age: 23 }) }).then((res) => res.json()) .then(data => console.log(data))
|
3.POST application/x-www-form-urlencoded
1 2 3 4 5 6 7 8 9 10 11
| fetch('http://localhost:5173/api/post', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'name=mmy&age=23' }).then(res => res.json()) .then(data => { console.log(data); })
|
4.上传进度
通过请求头的 header.get(‘Content-Type’)获取总数据字节数。
通过请求头的 body.getReader()获取一个流数据读取器,循环累计当前读取的字节数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| fetch('/api/txt').then(async (res) => { const response = res.clone(); const reader = res.body.getReader(); const total = res.headers.get('Content-Length'); let loaded = 0; while (true) { const { done, value } = await reader.read(); if (done) { break; } loaded += value.length || 0; progressDOM.innerHTML = `current progress: ${(loaded / total * 100).toFixed(2)}%`; } return response.text() }).then((data) => { console.log(data.length); }) .catch((e) => { console.log(e); })
|
5.中断请求
- 初始化一个终止器 abort
- 发起 fetch 时标记 signal 为 abort.signal
- 合适的时候调用 abort.abort()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const btnDOM = document.createElement('button'); btnDOM.innerHTML = 'abort post request' btnDOM.addEventListener('click', () => { console.log('abort'); abort.abort(); }) document.body.appendChild(btnDOM)
const abort = new AbortController(); fetch('http://localhost:5173/api/post', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, signal: abort.signal, body: 'name=mmy&age=23' }).then(res => res.json()) .then(data => { console.log(data); })
|
6.超时终止(手动实现)
超时——手动封装一个计时器,timeout 时终止
1 2 3 4 5
| const timeoutFn = (time = 1000) => { setTimeout(() => { abort.abort() }, time); }
|
1 2 3 4 5 6 7
| fetch('/api/txt', { headers: { 'Cookie': 'cookie_name=224712266' }, credentials: 'include' })
|
AJAX XML Axios Fetch 综合比较
Ajax 是一种早期的客户端和服务端异步数据交互技术,最重要的实现之一就是 XMLHttpRequest 对象,因此 Ajax 也可以理解成是一种使用 XML 通信、获取网络资源的技术。
Axios 是一种基于 XML 进行二次封装的类库,在 XML 基础上实现了新的功能,比如请求拦截器、响应拦截器。XML 本身异步逻辑是通过回调函数实现的,axios 基于 Promise 对 XML 封装,使得写法更加简洁易用
Fetch API 是一种更现代的网络请求技术,是一种不同于 Ajax 的技术,有如下优点
- 原生支持 Promise 和链式调用(XML 是回调函数实现的异步逻辑)
- 关注点分离的设计原则,API 更简洁、不同阶段的功能不过分耦合,写法简单(XML 需要进行多个参数和回调函数的配置)
- 丰富的请求和响应的数据类型支持,包括 JSON、FormData、Blob、ArrayBuffer 等,支持流式数据处理(XML 只支持文本和二进制数据)
- 可以和其他现代 Javascript API 配合使用如 Service Worker 等
- 跨域请求:
fetch
API 提供了一种简单而强大的解决方案——使用 CORS(跨域资源共享)头部实现跨域请求,而 XHR 则使用了一个叫做 XMLHttpRequest Level 2
的规范,在代码编写上相对较为繁琐。
缺点
没有原生支持 timeout 事件,需要自己实现
取消请求不方便,需要基于一个构造函数
相比于 Axios 第三方库使用比较繁琐,比如 request interceptor 和 response interceptor 需要自己实现
fetch
API 默认只支持 GET 和 POST 请求方法,而 XHR 则支持所有标准的 HTTP 请求方法。