第十阶段:网络请求与前端通信
-
XMLHttpRequest(简称 XHR)原生 AJAX:
AJAX :Asynchronous JavaScript And XML
异步 JS 和 XML,作用:不刷新整个页面,局部更新数据。
现在主流传输格式是 JSON,XML 基本淘汰。
同步 / 异步请求、请求状态码 status、readyState:
- 同步请求:请求会阻塞代码执行,直到请求完成。例如:
js
let xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/api', false);
xhr.send();
if (xhr.status === 200) {
console.log(xhr.responseText);
}
- 异步请求 :请求不会阻塞代码执行,通过监听
readystatechange事件获取请求状态。例如:
js
let xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/api', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText);
}
};
xhr.send();
- 请求状态码 status :表示请求的结果,如
200表示成功,404表示未找到资源等。 - readyState :表示请求的阶段,
0为未初始化,1为已调用open方法,2为已调用send方法,3为请求中,4为请求完成。
GET/POST 请求、请求头、请求参数、上传文件:
- GET 请求:将参数附加在 URL 后面。例如:
js
let xhr = new XMLHttpRequest();
let params = 'param1=value1¶m2=value2';
xhr.open('GET', 'https://example.com/api?' + params, true);
xhr.send();
- POST 请求:将参数放在请求体中。例如:
js
let xhr = new XMLHttpRequest();
let params = 'param1=value1¶m2=value2';
xhr.open('POST', 'https://example.com/api', true);
xhr.setRequestHeader('Content- type', 'application/x- www- form- urlencoded');
xhr.send(params);
- 请求头 :使用
setRequestHeader方法设置。例如设置Content- type头:
js
xhr.setRequestHeader('Content- type', 'application/json');
- 上传文件 :使用
FormData对象。例如:
html
<input type="file" id="fileInput">
<button id="uploadButton">上传</button>
<script>
let uploadButton = document.getElementById('uploadButton');
let fileInput = document.getElementById('fileInput');
uploadButton.addEventListener('click', function () {
let xhr = new XMLHttpRequest();
let formData = new FormData();
formData.append('file', fileInput.files[0]);
xhr.open('POST', 'https://example.com/upload', true);
xhr.send(formData);
});
</script>
- Fetch API「ES6+」:
fetch 基础语法、Request/Response 对象:
- 基础语法 :
fetch返回一个Promise。例如:
js
fetch('https://example.com/api')
.then(response => response.json())
.then(data => console.log(data));
- Request 对象:可用于创建自定义请求。例如:
js
let request = new Request('https://example.com/api', {
method: 'GET',
headers: {
'Content- type': 'application/json'
}
});
fetch(request)
.then(response => response.json())
.then(data => console.log(data));
- Response 对象 :包含请求的响应。
response.json()用于解析 JSON 数据,response.text()用于获取文本数据等。
Headers、GET/POST 传参、FormData:
- Headers :可通过
headers属性操作响应头。例如:
js
fetch('https://example.com/api')
.then(response => {
console.log(response.headers.get('Content- type'));
return response.json();
})
.then(data => console.log(data));
- GET 传参:将参数拼接到 URL 上。例如:
js
let params = new URLSearchParams();
params.append('param1', 'value1');
fetch('https://example.com/api?' + params.toString())
.then(response => response.json())
.then(data => console.log(data));
- POST 传参 :使用
body属性设置请求体。例如:
js
let data = { key: 'value' };
fetch('https://example.com/api', {
method: 'POST',
headers: {
'Content- type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
- FormData:用于上传文件和表单数据。例如:
html
<input type="file" id="fileInput">
<button id="uploadButton">上传</button>
<script>
let uploadButton = document.getElementById('uploadButton');
let fileInput = document.getElementById('fileInput');
uploadButton.addEventListener('click', function () {
let formData = new FormData();
formData.append('file', fileInput.files[0]);
fetch('https://example.com/upload', {
method: 'POST',
body: formData
})
.then(response => response.text())
.then(data => console.log(data));
});
</script>
fetch 坑:4xx/5xx 不会触发 reject、跨域处理:
- 4xx/5xx 不会触发 reject :
fetch只有在网络错误或请求被阻止时才会reject,4xx 和 5xx 状态码不会。需要手动检查response.ok。例如:
js
fetch('https://example.com/api')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
- 跨域处理 :与
XMLHttpRequest类似,通常通过 CORS 解决。服务端设置允许的源等响应头,前端无需特殊配置。
- 跨域问题详解:
- 同源策略定义 :同源策略是浏览器的安全策略,要求协议、域名、端口都相同,否则视为跨域。例如,
http://example.com:8080和http://example.com:8081不同源。 - CORS 跨域资源共享(服务端配置) :服务端通过设置响应头允许跨域。例如在 Node.js 中使用
express- cors中间件:
js
const express = require('express');
const cors = require('express- cors');
const app = express();
app.use(cors());
// 其他路由和处理
- JSONP 跨域原理 :利用
<script>标签没有跨域限制的特点。前端创建一个<script>标签,设置其src属性为跨域接口,并指定一个回调函数名。服务端返回包含该回调函数调用及数据的 JavaScript 代码。例如:
html
<script>
function handleResponse(data) {
console.log(data);
}
</script>
<script src="https://example.com/api?callback = handleResponse"></script>
代理服务器、iframe 跨域、postMessage「ES6+」:
- 代理服务器:前端请求本地代理服务器,代理服务器再请求跨域资源,然后将响应返回给前端,避免直接跨域。
- iframe 跨域 :通过在主页面嵌入一个与目标域同源的
iframe,主页面和iframe之间通过postMessage通信。例如:
html
<!-- 主页面-->
<iframe id="iframe" src="http://same- origin- domain.com/iframe.html"></iframe>
<script>
window.addEventListener('message', function (event) {
if (event.origin === 'http://same- origin- domain.com') {
console.log('收到来自 iframe 的消息:', event.data);
}
});
document.getElementById('iframe').contentWindow.postMessage('来自主页面的消息', 'http://same- origin- domain.com');
</script>
html
<!-- iframe.html-->
<script>
window.addEventListener('message', function (event) {
if (event.origin === 'http://parent- domain.com') {
console.log('收到来自主页面的消息:', event.data);
window.parent.postMessage('来自 iframe 的回复', 'http://parent- domain.com');
}
});
</script>
- postMessage「ES6+」 :可用于窗口之间的跨域通信,如
window.postMessage(data, targetOrigin)。
- WebSocket「ES6+」长连接实时通信:
- 是什么:WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,允许服务器主动向客户端推送数据。例如:
js
let socket = new WebSocket('ws://example.com/socket');
socket.onopen = function () {
console.log('连接已建立');
socket.send('Hello, server!');
};
socket.onmessage = function (event) {
console.log('收到消息:', event.data);
};
socket.onclose = function () {
console.log('连接已关闭');
};
- 为什么:传统的 HTTP 是请求- 响应模式,服务器不能主动推送数据。WebSocket 适用于实时通信场景,如聊天应用、实时数据更新等。
- 怎么用 :创建
WebSocket实例,监听open(连接建立)、message(收到消息)、close(连接关闭)等事件,使用send方法发送数据。
- 简单文件上传、下载前端实现:
- 文件上传 :已在 XMLHttpRequest 和 Fetch API 部分介绍,通过
FormData对象实现。 - 文件下载 :可以创建一个
<a>标签,设置其href属性为文件的 URL,并设置download属性指定下载文件名。例如:
html
<button id="downloadButton">下载文件</button>
<script>
let downloadButton = document.getElementById('downloadButton');
downloadButton.addEventListener('click', function () {
let a = document.createElement('a');
a.href = 'https://example.com/file.pdf';
a.download = 'example.pdf';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
});
</script>
实战练习:
- fetch 发送表单 FormData 上传数据
- JSONP 简易跨域封装
- WebSocket 简易聊天室收发消息
思考题:
- fetch 为什么 4xx/5xx 不会进入 catch?
- 同源策略限制哪些操作?
- WebSocket 和 AJAX 轮询区别?