当前位置: 首页 > ds >正文

JavaScript的 fetch() 方法 笔记250810

JavaScript的 fetch() 方法 笔记250810

JavaScript 的 fetch() 方法是现代浏览器提供的用于发起网络请求的 API。它基于 Promise 设计,相比传统的 XMLHttpRequest 更简洁强大。以下是核心知识点和示例:


基础语法

fetch(url [, options]).then(response => /* 处理响应 */).catch(error => /* 处理错误 */);
  • url:请求的目标地址(必需)
  • options:配置对象(可选),包含:
    • method:请求方法(GET, POST, PUT 等),默认为 GET
    • headers:请求头(如 { 'Content-Type': 'application/json' }
    • body:请求体数据(POST/PUT 时使用,需字符串化)
    • credentials:是否发送 cookies(includesame-originomit
    • mode:跨域模式(corsno-corssame-origin

关键特性

  1. 返回 Promise 对象

    • 网络错误(如无法连接)会触发 catch
    • HTTP 错误状态(如 404、500)不会触发 catch,需手动检查 response.ok
  2. 需解析响应体
    响应对象 Response 提供多种解析方法:

    • response.json() → 解析为 JSON 对象
    • response.text() → 解析为文本
    • response.blob() → 解析为二进制 Blob
  3. 默认不携带 Cookies
    需显式设置 credentials: 'include'


使用示例
     1. GET 请求(获取 JSON 数据)

fetch('https://api.example.com/data').then(response => {if (!response.ok) throw new Error('网络响应异常');return response.json(); // 解析 JSON}).then(data => console.log(data)).catch(error => console.error('请求失败:', error));

     2. POST 请求(提交 JSON)

fetch('https://api.example.com/users', {method: 'POST',headers: {'Content-Type': 'application/json','Authorization': 'Bearer YOUR_TOKEN'},body: JSON.stringify({ name: 'Alice', age: 30 }), // 对象转 JSON 字符串credentials: 'include' // 携带 Cookies
}).then(response => response.json()).then(data => console.log('创建成功:', data));

     3. 错误处理(统一捕获)

async function fetchData() {try {const response = await fetch('https://invalid-url');if (!response.ok) throw new Error(`HTTP 错误! 状态码: ${response.status}`);const data = await response.json();return data;} catch (error) {console.error('请求出错:', error);}
}

     4. 取消请求(使用 AbortController

const controller = new AbortController();
const { signal } = controller;// 设置超时取消(5秒)
setTimeout(() => controller.abort(), 5000);fetch('https://slow-api.com/data', { signal }).then(response => response.json()).catch(err => {if (err.name === 'AbortError') {console.log('请求被取消!');}});

常见问题

  1. 跨域请求(CORS)
    服务端需设置 Access-Control-Allow-Origin 等响应头,否则请求会被浏览器拦截。

  2. HTTP 错误状态处理
    务必检查 response.okresponse.status,因为 fetch 只在网络故障时拒绝 Promise。

  3. 请求体格式
    body 必须是字符串(如 JSON.stringify())、FormDataBlob 对象。


兼容性

  • 所有现代浏览器(Chrome 42+、Firefox 39+、Safari 10.1+)均支持。
  • 旧浏览器(如 IE)需使用 polyfill(如 whatwg-fetch)。

通过 fetch(),你可以更优雅地处理网络请求,结合 async/await 语法能大幅提升代码可读性。

















fetch() 函数详解

JavaScript 的 fetch() 函数详解

fetch() 是现代 JavaScript 中用于发起网络请求的核心 API,基于 Promise 设计,提供了比传统 XMLHttpRequest 更简洁强大的功能。下面从多个维度深入解析:


     一、基础架构

fetch(resource [, init]).then(response => { /* 处理响应 */ }).catch(error => { /* 处理错误 */ });
参数类型说明
resourceString/Request请求 URL 或 Request 对象实例
initObject可选配置对象(见下文)

     二、配置对象(init)详解

{// 请求方法(默认:'GET')method: 'POST', // 请求头headers: {'Content-Type': 'application/json','Authorization': 'Bearer token123'},// 请求体(GET/HEAD 请求不能包含)body: JSON.stringify({ key: 'value' }),// 凭证控制credentials: 'include', // 可选值: 'omit'(默认)、'same-origin'、'include'// 请求模式mode: 'cors', // 可选值: 'cors'(默认)、'no-cors'、'same-origin'// 缓存策略cache: 'no-cache', // 可选值: 'default'、'no-store'、'reload' 等// 重定向策略redirect: 'follow', // 可选值: 'follow'(默认)、'error'、'manual'// 引用策略referrerPolicy: 'no-referrer',// 完整性校验integrity: 'sha256-BpfBw7ivV8q2jLiT13...',// 信号控制(用于取消请求)signal: abortController.signal
}

     三、响应处理(Response 对象)
fetch() 返回的 Promise 解析为 Response 对象,包含以下重要属性和方法:

属性:

  • response.ok:布尔值,HTTP 状态码 200-299 时为 true
  • response.status:HTTP 状态码(如 200、404)
  • response.statusText:状态文本(如 “OK”)
  • response.headers:响应头对象
  • response.url:最终请求 URL(考虑重定向后)

解析方法:

  • response.json() → 解析为 JSON 对象
  • response.text() → 解析为字符串
  • response.blob() → 解析为 Blob 对象
  • response.arrayBuffer() → 解析为 ArrayBuffer
  • response.formData() → 解析为 FormData 对象

注意:每个解析方法只能使用一次,如需多次使用需先克隆响应:
const cloned = response.clone()


     四、完整请求生命周期示例

         1. GET 请求(带错误处理)

fetch('https://api.example.com/data').then(response => {// 检查网络响应状态if (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);}// 检查内容类型const contentType = response.headers.get('content-type');if (!contentType.includes('application/json')) {throw new TypeError("非JSON响应");}return response.json();}).then(data => {console.log('获取数据:', data);}).catch(error => {console.error('请求失败:', error);});

         2. POST 请求(提交表单数据)

const formData = new FormData();
formData.append('username', 'john');
formData.append('avatar', fileInput.files[0]);fetch('/api/user', {method: 'POST',body: formData, // 自动设置 multipart/form-datacredentials: 'include'
});

         3. 带超时的请求(AbortController)

const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);fetch('https://slow-api.com/data', {signal: controller.signal
}).then(response => response.json()).then(data => {clearTimeout(timeoutId);console.log(data);}).catch(err => {if (err.name === 'AbortError') {console.log('请求超时取消');} else {console.error('其他错误:', err);}});

     五、高级应用场景

         1. 并行请求(Promise.all)

const urls = ['/api/user', '/api/posts', '/api/notifications'];Promise.all(urls.map(url => fetch(url).then(res => res.json())
)).then(([user, posts, notifications]) => {console.log('全部数据加载完成');});

         2. 流式处理(大文件处理)

fetch('https://example.com/large-video.mp4').then(response => {const reader = response.body.getReader();const stream = new ReadableStream({start(controller) {function push() {reader.read().then(({ done, value }) => {if (done) {controller.close();return;}controller.enqueue(value);push();});}push();}});return new Response(stream);}).then(response => response.blob()).then(blob => {videoElement.src = URL.createObjectURL(blob);});

         3. 自定义请求对象

const myRequest = new Request('/api/data', {method: 'PUT',headers: new Headers({'X-Custom-Header': 'value'}),body: JSON.stringify({ update: 'new value' })
});fetch(myRequest).then(/* ... */);

     六、关键注意事项

  1. CORS 限制
    跨域请求需要服务器设置正确的响应头(如 Access-Control-Allow-Origin

  2. Cookie 策略
    默认不发送/接收 cookies,需显式设置 credentials: 'include'

  3. 错误处理差异

    • 网络故障:触发 catch 或 Promise 拒绝
    • HTTP 错误(4xx/5xx):需手动检查 response.ok
  4. 数据格式处理

    • JSON 数据需使用 JSON.stringify() 转换
    • FormData 会自动设置正确的 Content-Type
  5. 性能优化

    • 使用 AbortController 实现请求超时和取消
    • 大文件处理优先使用流式 API

     七、浏览器兼容性

浏览器支持版本备注
Chrome42+完整支持
Firefox39+完整支持
Safari10.1+部分高级特性需更新版本
Edge14+完整支持
Internet Explorer不支持需使用 polyfill

Polyfill 方案

<!-- 通过 npm 安装 -->
<script src="https://cdn.jsdelivr.net/npm/whatwg-fetch@3.6.2/dist/fetch.umd.min.js"></script>

     八、最佳实践总结

  1. 始终检查 response.okresponse.status
  2. 使用 async/await 提升代码可读性
  3. 为敏感请求设置超时限制(AbortController)
  4. 根据响应内容类型选择正确的解析方法
  5. 跨域请求确保服务器配置 CORS 头部
  6. 需要 cookies 认证时设置 credentials: 'include'

通过掌握这些核心概念,您可以高效安全地使用 fetch() 处理各种网络请求场景。

















JavaScript Fetch API 全面指南

Fetch API 是现代 JavaScript 中用于网络请求的核心技术,它基于 Promise 设计,提供了比传统 XMLHttpRequest 更强大、更灵活的解决方案。本指南将深入探讨 Fetch API 的各个方面。

一、Fetch API 核心概念

     1. 基本架构

fetch(resource [, init]).then(response => { /* 处理响应 */ }).catch(error => { /* 处理错误 */ });
  • resource:请求资源(URL 字符串或 Request 对象)
  • init:配置对象(可选)

     2. 与传统 XHR 的对比

特性Fetch APIXMLHttpRequest
语法Promise-based回调函数
请求取消AbortControllerxhr.abort()
流式处理原生支持有限支持
CORS 处理更简洁较复杂
请求体/响应体类型丰富(Blob、FormData等)有限

二、请求配置详解

     1. 完整配置选项

{method: 'POST',          // HTTP 方法headers: {               // 请求头'Content-Type': 'application/json','Authorization': 'Bearer token'},body: JSON.stringify(data), // 请求体mode: 'cors',            // 跨域模式cache: 'no-cache',       // 缓存策略credentials: 'include',  // 凭证控制redirect: 'follow',      // 重定向策略referrerPolicy: 'no-referrer', // 来源策略integrity: 'sha256-...', // 子资源完整性signal: abortSignal      // 中止信号
}

     2. 请求体支持类型

  • JSON 数据JSON.stringify({ key: 'value' })
  • FormDatanew FormData(formElement)
  • URLSearchParamsnew URLSearchParams({ key: 'value' })
  • Blob/BufferSource:二进制数据
  • 字符串:普通文本

三、响应处理深度解析

     1. Response 对象结构

{ok: true,                // 状态码 200-299 时为 truestatus: 200,             // HTTP 状态码statusText: 'OK',        // 状态文本headers: Headers {},     // 响应头对象url: 'https://...',      // 最终 URL(含重定向)type: 'cors',            // 响应类型redirected: false,       // 是否重定向body: ReadableStream,    // 可读流bodyUsed: false          // 是否已读取
}

     2. 响应数据解析方法

// JSON 数据
const data = await response.json();// 文本内容
const text = await response.text();// Blob 对象(如图片)
const blob = await response.blob();// ArrayBuffer(二进制数据)
const buffer = await response.arrayBuffer();// FormData(表单数据)
const formData = await response.formData();

重要:每个解析方法只能调用一次,如需多次使用需先克隆响应:

const clone1 = response.clone();
const clone2 = response.clone();
const data1 = await clone1.json();
const data2 = await clone2.text();

四、高级应用场景

     1. 请求取消(AbortController)

const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);try {const response = await fetch(url, {signal: controller.signal});clearTimeout(timeoutId);// 处理响应...
} catch (err) {if (err.name === 'AbortError') {console.log('请求被取消');} else {console.error('请求错误:', err);}
}

     2. 流式数据处理

const response = await fetch('https://example.com/large-file');
const reader = response.body.getReader();while (true) {const { done, value } = await reader.read();if (done) break;console.log('接收到数据块:', value);// 处理数据块(如实时渲染)
}

     3. 上传进度监控

const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];const xhr = new XMLHttpRequest();
xhr.upload.onprogress = (event) => {const percent = Math.round((event.loaded / event.total) * 100);console.log(`上传进度: ${percent}%`);
};// 使用 Fetch + Progress (实验性)
const response = await fetch(url, {method: 'POST',body: file,duplex: 'half' // 必需
});// 使用 ReadableStream 模拟进度
const progressStream = new TransformStream({transform(chunk, controller) {loaded += chunk.byteLength;updateProgress(loaded, total);controller.enqueue(chunk);}
});

     4. 自定义请求拦截

// 全局请求拦截
const originalFetch = window.fetch;window.fetch = async (resource, init) => {// 请求前处理(如添加认证头)init.headers = {...init.headers,'X-Auth-Token': getToken()};const start = Date.now();const response = await originalFetch(resource, init);const duration = Date.now() - start;// 响应后处理(如日志记录)logRequest(resource, response.status, duration);return response;
};

五、错误处理最佳实践

     1. 综合错误处理方案

async function safeFetch(url, options) {try {const response = await fetch(url, options);// 处理 HTTP 错误状态if (!response.ok) {const error = new Error(`HTTP 错误! 状态码: ${response.status}`);error.status = response.status;throw error;}// 根据内容类型解析const contentType = response.headers.get('content-type');if (contentType.includes('application/json')) {return await response.json();} else if (contentType.includes('text/')) {return await response.text();} else {return await response.blob();}} catch (error) {// 分类处理不同错误类型if (error.name === 'AbortError') {console.warn('请求被取消');} else if (error.name === 'TypeError') {console.error('网络错误或 CORS 问题');} else {console.error('请求失败:', error);}// 返回统一错误格式return {success: false,error: error.message,status: error.status || 0};}
}

六、性能优化技巧

  1. 请求合并

    // 并行请求
    const [user, posts] = await Promise.all([fetch('/api/user').then(r => r.json()),fetch('/api/posts').then(r => r.json())
    ]);
    
  2. 缓存策略

    fetch(url, {cache: 'force-cache' // 优先使用缓存
    });// 或者
    fetch(url, {cache: 'reload' // 绕过缓存
    });
    
  3. 数据压缩

    fetch(url, {headers: {'Accept-Encoding': 'gzip, deflate, br'}
    });
    
  4. HTTP/2 服务端推送

    // 服务器端配置
    Link: </styles.css>; rel=preload; as=style
    

七、安全实践

  1. CSRF 防护

    fetch(url, {headers: {'X-CSRF-Token': getCSRFToken()}
    });
    
  2. 内容安全策略(CSP)

    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'">
    
  3. 子资源完整性(SRI)

    fetch('https://cdn.example.com/library.js', {integrity: 'sha384-...'
    });
    
  4. CORS 安全配置

    // 服务器端响应头
    Access-Control-Allow-Origin: https://yourdomain.com
    Access-Control-Allow-Credentials: true
    

八、浏览器兼容性与 Polyfill

浏览器支持版本注意事项
Chrome42+完整支持
Firefox39+完整支持
Safari10.1+部分高级特性需更新版本
Edge14+完整支持
IE不支持需使用 polyfill

Polyfill 方案

<!-- 使用 whatwg-fetch polyfill -->
<script src="https://cdn.jsdelivr.net/npm/whatwg-fetch@3.6.2/dist/fetch.umd.min.js"></script><!-- 或通过 npm 安装 -->
npm install whatwg-fetch
// 在应用入口引入
import 'whatwg-fetch';

九、实际应用示例

     1. 文件分片上传

async function uploadFile(file) {const CHUNK_SIZE = 5 * 1024 * 1024; // 5MBconst totalChunks = Math.ceil(file.size / CHUNK_SIZE);for (let i = 0; i < totalChunks; i++) {const chunk = file.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE);const formData = new FormData();formData.append('file', chunk);formData.append('chunkIndex', i);formData.append('totalChunks', totalChunks);formData.append('fileName', file.name);await fetch('/api/upload', {method: 'POST',body: formData});console.log(`上传分片 ${i + 1}/${totalChunks}`);}// 通知服务器合并文件await fetch('/api/merge', {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({ fileName: file.name })});
}

     2. 带重试机制的请求

async function fetchWithRetry(url, options, maxRetries = 3) {for (let i = 0; i < maxRetries; i++) {try {const response = await fetch(url, options);if (response.ok) return response;// 服务器错误时重试if (response.status >= 500) {throw new Error(`服务器错误: ${response.status}`);}return response;} catch (error) {if (i === maxRetries - 1) throw error;// 指数退避重试const delay = Math.pow(2, i) * 1000 + Math.random() * 1000;await new Promise(resolve => setTimeout(resolve, delay));}}
}

十、未来发展趋势

  1. Fetch 元数据

    // 实验性功能
    fetch(url, {priority: 'high' // 请求优先级
    });
    
  2. 请求诊断 API

    const resourceTiming = performance.getEntriesByName(url)[0];
    console.log('DNS 时间:', resourceTiming.domainLookupEnd - resourceTiming.domainLookupStart);
    
  3. WebTransport

    // 下一代传输协议(基于 HTTP/3)
    const transport = new WebTransport('https://example.com');
    const stream = await transport.createBidirectionalStream();
    
  4. 后台同步 API

    // 服务工作者中使用
    self.registration.sync.register('sync-data');
    

总结

Fetch API 是现代 Web 开发的基石,提供了强大而灵活的网络请求能力。关键要点:

  1. 始终检查 response.ok 处理 HTTP 错误
  2. 使用 AbortController 实现请求取消和超时
  3. 根据内容类型选择合适的解析方法
  4. 重要请求实现重试和回退机制
  5. 敏感请求添加 CSRF 保护
  6. 大文件处理使用流式 API
  7. 兼容旧浏览器使用 polyfill

随着 Web 平台的发展,Fetch API 将继续演进,为开发者提供更强大的网络能力。掌握这些技术将使您能够构建更健壮、更高效的 Web 应用。

















fetch 的配置项

Fetch API 配置项全面指南

Fetch API 的配置项是控制 HTTP 请求行为的核心参数,通过 fetch(url, options) 的第二个参数传递。以下是所有可用配置项的详细说明和示例:

一、基础配置项

配置项类型默认值说明
methodString‘GET’HTTP 方法 (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
headersObject{}请求头对象
bodyVariousnull请求体数据(Blob, BufferSource, FormData, URLSearchParams, String)
modeString‘cors’请求模式:‘cors’, ‘no-cors’, ‘same-origin’, ‘navigate’
credentialsString‘same-origin’凭证控制:‘omit’, ‘same-origin’, ‘include’
cacheString‘default’缓存策略:‘default’, ‘no-store’, ‘reload’, ‘no-cache’, ‘force-cache’, ‘only-if-cached’
redirectString‘follow’重定向策略:‘follow’, ‘error’, ‘manual’
referrerString‘about:client’来源页面 URL
referrerPolicyString‘no-referrer-when-downgrade’来源策略(控制 Referer 头)
integrityString‘’子资源完整性校验(如 ‘sha256-BpfBw7ivV8q2jLiT13…’)
signalAbortSignalnull用于取消请求的信号对象
priorityString‘auto’请求优先级:‘high’, ‘low’, ‘auto’(实验性功能)
duplexString-双工模式:‘half’(实验性,用于支持进度监控)

二、配置项详解与示例

     1. 基础请求配置

// POST 请求示例
fetch('/api/data', {method: 'POST',headers: {'Content-Type': 'application/json','Authorization': 'Bearer token123'},body: JSON.stringify({ key: 'value' })
});

     2. 跨域与凭证控制

// 跨域请求带凭证
fetch('https://cross-origin.com/api', {mode: 'cors', // 必需跨域模式credentials: 'include', // 包含 cookiesheaders: {'X-Requested-With': 'XMLHttpRequest'}
});

     3. 缓存控制策略

// 强制缓存验证
fetch('/static/data.json', {cache: 'no-cache' // 使用缓存但验证新鲜度
});// 完全绕过缓存
fetch('/latest-data', {cache: 'reload' // 忽略缓存,直接从服务器获取
});// 仅使用缓存
fetch('/cached-data', {cache: 'only-if-cached' // 仅从缓存读取,不发送请求
});

     4. 重定向处理

// 禁止重定向
fetch('/redirect-endpoint', {redirect: 'error' // 遇到重定向时抛出错误
});// 手动处理重定向
fetch('/redirect', {redirect: 'manual'
}).then(response => {if (response.type === 'opaqueredirect') {// 手动处理重定向window.location = response.url;}
});

     5. 请求完整性校验

// 确保资源未被篡改
fetch('/critical-script.js', {integrity: 'sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC'
});

     6. 取消请求(AbortController)

const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);fetch('/slow-api', {signal: controller.signal
}).then(response => {clearTimeout(timeoutId);return response.json();}).catch(err => {if (err.name === 'AbortError') {console.log('请求超时取消');}});

     7. 请求优先级(实验性)

// 高优先级请求(如图片/关键数据)
fetch('/hero-image.jpg', {priority: 'high'
});// 低优先级请求(如日志、分析)
fetch('/analytics', {priority: 'low'
});

三、body 数据类型处理

     1. 不同数据类型的 body 配置

数据类型Content-Type 头示例
JSON 对象application/jsonbody: JSON.stringify({key: 'value'})
FormDatamultipart/form-databody: new FormData(formElement)
URLSearchParamsapplication/x-www-form-urlencodedbody: new URLSearchParams({key: 'value'})
Blob/File自动设置body: new Blob([JSON.stringify(data)])
ArrayBuffer根据内容设置body: new Uint8Array([1,2,3]).buffer
字符串text/plainbody: 'plain text'

     2. 完整示例

// FormData 上传文件
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('comment', '文件说明');fetch('/upload', {method: 'POST',body: formData // 自动设置 multipart/form-data
});// URLSearchParams 提交表单
const params = new URLSearchParams();
params.append('search', 'JavaScript');
params.append('page', '1');fetch('/search', {method: 'POST',body: params // 自动设置 application/x-www-form-urlencoded
});// Blob 上传
const blob = new Blob([JSON.stringify({ data: 'value' })], {type: 'application/json'
});fetch('/submit', {method: 'POST',body: blob
});

四、headers 配置详解

     1. 设置请求头

// 使用 Headers 对象
const headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('X-Custom-Header', 'value');// 或使用普通对象
fetch('/api', {headers: {'Content-Type': 'application/json','Authorization': `Bearer ${token}`,'X-Request-ID': uuidv4()}
});

     2. 禁止的请求头
以下请求头由浏览器控制,无法通过 JavaScript 设置:

  • Accept-Charset
  • Accept-Encoding
  • Access-Control-Request-Headers
  • Access-Control-Request-Method
  • Connection
  • Content-Length
  • Cookie
  • Cookie2
  • Date
  • DNT
  • Expect
  • Host
  • Keep-Alive
  • Origin
  • Referer
  • TE
  • Trailer
  • Transfer-Encoding
  • Upgrade
  • Via

五、高级配置场景

     1. 流式上传(带进度)

// 创建带进度监控的可读流
function createProgressStream(stream, onProgress) {let loaded = 0;return new ReadableStream({start(controller) {const reader = stream.getReader();async function push() {const { done, value } = await reader.read();if (done) {controller.close();return;}loaded += value.length;onProgress(loaded);controller.enqueue(value);push();}push();}});
}const fileStream = file.stream();
const progressStream = createProgressStream(fileStream, loaded => {console.log(`已上传: ${loaded} 字节`);
});fetch('/upload', {method: 'POST',headers: { 'Content-Type': file.type },body: progressStream,duplex: 'half' // 必需参数
});

     2. 服务端事件 (SSE) 替代方案

// 使用 Fetch 实现类 SSE 功能
async function streamEvents(url) {const response = await fetch(url, {headers: { 'Accept': 'text/event-stream' }});const reader = response.body.getReader();const decoder = new TextDecoder();while (true) {const { done, value } = await reader.read();if (done) break;const chunk = decoder.decode(value, { stream: true });chunk.split('\n\n').forEach(event => {if (event.trim()) {const [id, type, data] = parseEvent(event);handleEvent({ id, type, data });}});}
}

     3. HTTP/2 服务器推送

// 利用服务器推送(需要服务端支持)
fetch('/main-resource', {headers: { 'Accept': 'multipart/mixed' }
}).then(response => {// 处理主资源和推送资源const reader = response.body.getReader();// ...解析多部分响应...
});

六、配置项最佳实践

  1. 始终设置超时

    function fetchWithTimeout(url, options, timeout = 8000) {const controller = new AbortController();setTimeout(() => controller.abort(), timeout);return fetch(url, { ...options, signal: controller.signal });
    }
    
  2. JSON 请求封装

    async function jsonFetch(url, method = 'GET', data = null) {const options = {method,headers: { 'Content-Type': 'application/json' },credentials: 'include'};if (data) options.body = JSON.stringify(data);const response = await fetch(url, options);if (!response.ok) throw new Error(`HTTP ${response.status}`);return response.json();
    }
    
  3. 安全请求配置

    fetch('/sensitive', {method: 'POST',credentials: 'include',integrity: 'sha256-...', // 完整性校验headers: {'Content-Type': 'application/json','X-CSRF-Token': getCSRFToken(), // CSRF 防护'Strict-Transport-Security': 'max-age=31536000' // HSTS}
    });
    

七、浏览器兼容性注意事项

  1. 实验性功能支持

    • priority:仅 Chrome 65+ 支持
    • duplex:仅 Chrome 105+ 支持
    • integrity:所有现代浏览器支持
  2. Polyfill 方案

    <!-- 基础 Fetch polyfill -->
    <script src="https://cdn.jsdelivr.net/npm/whatwg-fetch@3.6.2/dist/fetch.umd.min.js"></script><!-- AbortController polyfill -->
    <script src="https://cdn.jsdelivr.net/npm/abortcontroller-polyfill@1.7.3/dist/abortcontroller-polyfill-only.min.js"></script>
    
  3. Node.js 支持

    // Node.js 18+ 实验性支持
    const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));// 或使用 node-fetch 库
    const fetch = require('node-fetch');
    

八、常见问题解决方案

  1. CORS 问题

    fetch('https://cross-origin.com/api', {mode: 'cors',headers: {// 服务器需要配置 Access-Control-Allow-Origin}
    });
    
  2. Cookie 未发送

    fetch('/auth', {credentials: 'include' // 必需
    });
    
  3. 多次读取响应体

    fetch('/data').then(response => {const clone = response.clone(); // 克隆响应return Promise.all([response.json(),clone.text()]);});
    
  4. 上传进度监控

    // 使用 XMLHttpRequest 替代或实验性 Streams API
    

掌握 Fetch API 的配置项是优化网络请求的关键,合理组合这些选项可以实现从简单请求到复杂场景的全方位控制。根据目标浏览器环境选择合适的配置,并始终考虑安全性和错误处理,将大大提升应用的网络请求质量。

















fetch 的 Response

Response 对象详解

在 JavaScript Fetch API 中,Response 对象代表了对 HTTP 请求的响应。它包含了服务器返回的所有信息,并提供了多种方法来处理响应数据。以下是 Response 对象的全面解析:


一、Response 对象属性详解

属性类型说明
okBoolean状态码在 200-299 范围内时为 true
statusNumberHTTP 状态码(如 200, 404, 500 等)
statusTextString状态文本(如 “OK”, “Not Found”)
urlString最终响应 URL(考虑重定向后的地址)
headersHeaders 对象响应头信息
typeString响应类型(basic, cors, error, opaque, opaqueredirect
redirectedBoolean是否经过重定向
bodyReadableStream可读流形式的响应体
bodyUsedBoolean是否已读取过响应体

     示例:访问属性

fetch('https://api.example.com/data').then(response => {console.log('状态码:', response.status);console.log('状态文本:', response.statusText);console.log('内容类型:', response.headers.get('content-type'));console.log('响应URL:', response.url);console.log('是否成功:', response.ok);});

二、响应体处理方法

Response 对象提供多种方法解析响应体,每个方法只能调用一次

     1. response.json()
将响应体解析为 JSON 对象

fetch('/api/data').then(response => response.json()).then(data => console.log(data));

     2. response.text()
获取响应体文本内容

fetch('/text-file.txt').then(response => response.text()).then(text => console.log(text));

     3. response.blob()
获取二进制 Blob 对象(适合文件下载)

fetch('/image.png').then(response => response.blob()).then(blob => {const img = new Image();img.src = URL.createObjectURL(blob);document.body.appendChild(img);});

     4. response.arrayBuffer()
获取原始二进制 ArrayBuffer(适合音频/视频处理)

fetch('/audio.mp3').then(response => response.arrayBuffer()).then(buffer => {const audioCtx = new AudioContext();audioCtx.decodeAudioData(buffer, audio => {// 处理音频数据});});

     5. response.formData()
解析表单数据

fetch('/form-submit').then(response => response.formData()).then(formData => {console.log(formData.get('username'));});

三、Headers 对象操作

response.headers 提供对响应头的访问:

// 获取单个头信息
const contentType = response.headers.get('content-type');// 检查头是否存在
if (response.headers.has('X-Custom-Header')) {// ...
}// 遍历所有头信息
response.headers.forEach((value, name) => {console.log(`${name}: ${value}`);
});// 转为普通对象
const headersObj = Object.fromEntries(response.headers.entries());

四、克隆响应对象

当需要多次使用响应体时(如同时获取 JSON 和原始文本),需先克隆响应:

fetch('/api/data').then(response => {// 创建两个克隆const jsonClone = response.clone();const textClone = response.clone();return Promise.all([jsonClone.json(),textClone.text()]);}).then(([data, rawText]) => {console.log('Parsed data:', data);console.log('Raw text:', rawText);});

五、响应类型(Response.type)

类型说明
basic同源响应
cors有效的跨域响应(包含 CORS 头)
error网络错误(无法完成请求)
opaque跨域请求但未返回 CORS 头(无法读取状态/头信息)
opaqueredirect重定向模式设为 “manual” 时的重定向响应

检测响应类型:

fetch('https://cross-origin.com/data').then(response => {if (response.type === 'cors') {console.log('有效的跨域响应');} else if (response.type === 'opaque') {console.log('受限的跨域响应');}});

六、自定义 Response 对象

可手动创建 Response 对象用于测试或 Service Worker:

// 创建简单文本响应
const textResponse = new Response('Hello, world!', {status: 200,headers: new Headers({'Content-Type': 'text/plain'})
});// 创建 JSON 响应
const jsonResponse = new Response(JSON.stringify({ data: 123 }), {status: 200,headers: { 'Content-Type': 'application/json' }
});// 创建错误响应
const errorResponse = Response.error(); // status=0, type=error

七、流式处理响应体

处理大文件时使用流式 API:

fetch('/large-video.mp4').then(response => {const reader = response.body.getReader();const stream = new ReadableStream({start(controller) {function push() {reader.read().then(({ done, value }) => {if (done) {controller.close();return;}controller.enqueue(value);push();});}push();}});return new Response(stream);}).then(response => response.blob()).then(blob => {// 处理大文件});

八、在 Service Worker 中的高级用法

     1. 缓存响应

self.addEventListener('fetch', event => {event.respondWith(caches.match(event.request).then(cached => cached || fetch(event.request)));
});

     2. 合成响应

self.addEventListener('fetch', event => {if (event.request.url.endsWith('/custom-data')) {const data = { timestamp: Date.now() };const jsonResponse = new Response(JSON.stringify(data), { headers: { 'Content-Type': 'application/json' } });event.respondWith(jsonResponse);}
});

九、注意事项与最佳实践

  1. 响应体单次读取

    // 错误示例:尝试多次读取
    fetch('/data').then(response => {const data = response.json();  // 第一次读取const text = response.text(); // 错误!bodyUsed 已为 true});
    
  2. HTTP 错误处理

    fetch('/api').then(response => {if (!response.ok) {throw new Error(`HTTP ${response.status}`);}return response.json();});
    
  3. 内容类型验证

    fetch('/data').then(response => {const contentType = response.headers.get('content-type');if (!contentType.includes('application/json')) {throw new TypeError("Expected JSON response");}return response.json();});
    
  4. 超时控制

    const controller = new AbortController();
    setTimeout(() => controller.abort(), 5000);fetch('/slow-api', { signal: controller.signal }).then(response => response.json()).catch(err => {if (err.name === 'AbortError') {console.log('Request timed out');}});
    

十、浏览器兼容性

浏览器支持版本备注
Chrome42+完整支持
Firefox39+完整支持
Safari10.1+流式处理需更新版本
Edge14+完整支持
Node.js18+实验性支持(需启用 flag)

Polyfill 方案

npm install node-fetch # Node.js 环境
npm install whatwg-fetch # 浏览器环境

总结

Response 对象是 Fetch API 的核心组件,提供了:

  • 访问 HTTP 响应元数据(状态码、头信息等)
  • 多种响应体解析方法(JSON、文本、Blob 等)
  • 流式处理能力(适合大文件)
  • 克隆机制(允许多次使用响应)
  • 自定义响应创建能力

掌握 Response 对象的正确使用方式,结合适当的错误处理和流控制,可以构建高效可靠的网络请求处理逻辑,特别是在处理大型数据集或媒体文件时表现出色。

















XMLHttpRequest

XMLHttpRequest 全面解析

XMLHttpRequest (XHR) 是浏览器提供的 JavaScript API,用于在客户端和服务器之间传输数据。它是 AJAX (Asynchronous JavaScript and XML) 技术的核心基础,虽然现在有更现代的 Fetch API,但 XHR 仍然广泛使用且功能强大。

     一、基础架构与核心概念

         1. 创建实例

const xhr = new XMLHttpRequest();

         2. 请求生命周期

  1. 创建对象:new XMLHttpRequest()
  2. 配置请求:.open(method, url, async)
  3. 设置回调:.onreadystatechange
  4. 发送请求:.send(body)
  5. 处理响应:检查 readyStatestatus

     二、核心方法与属性

         1. 请求配置方法

// 初始化请求
xhr.open(method, url, async, user, password);// 设置请求头
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Authorization', 'Bearer token');// 设置响应类型
xhr.responseType = 'json'; // 可选: 'text', 'arraybuffer', 'blob', 'document'

         2. 状态属性

// 请求状态 (0-4)
xhr.readyState;// HTTP 状态码
xhr.status;// 状态文本
xhr.statusText;// 响应数据
xhr.response;
xhr.responseText; // 当 responseType 为 'text' 时
xhr.responseXML;  // 当响应是 XML 时

         3. readyState 状态详解

状态描述
0UNSENT对象已创建,open() 未调用
1OPENEDopen() 已调用
2HEADERS_RECEIVED收到响应头
3LOADING接收响应体中
4DONE请求完成(成功或失败)

     三、完整请求示例

         1. GET 请求

const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true);xhr.onreadystatechange = function() {if (xhr.readyState === 4) {if (xhr.status === 200) {console.log('成功:', JSON.parse(xhr.responseText));} else {console.error('错误:', xhr.statusText);}}
};xhr.onerror = function() {console.error('网络错误');
};xhr.send();

         2. POST 请求 (JSON)

const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://api.example.com/submit', true);
xhr.setRequestHeader('Content-Type', 'application/json');xhr.onload = function() {if (xhr.status >= 200 && xhr.status < 300) {console.log('提交成功:', xhr.response);}
};const data = JSON.stringify({ name: 'John', age: 30 });
xhr.send(data);

     四、高级功能

         1. 超时处理

xhr.timeout = 5000; // 5秒超时
xhr.ontimeout = function() {console.error('请求超时');
};

         2. 进度监控

// 上传进度
xhr.upload.onprogress = function(event) {if (event.lengthComputable) {const percent = (event.loaded / event.total) * 100;console.log(`上传: ${percent.toFixed(2)}%`);}
};// 下载进度
xhr.onprogress = function(event) {if (event.lengthComputable) {const percent = (event.loaded / event.total) * 100;console.log(`下载: ${percent.toFixed(2)}%`);}
};

         3. 中止请求

const xhr = new XMLHttpRequest();
// ...配置请求...// 中止请求
document.getElementById('cancelBtn').addEventListener('click', () => {xhr.abort();console.log('请求已取消');
});

         4. 文件上传

const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('username', 'john_doe');const xhr = new XMLHttpRequest();
xhr.open('POST', '/upload', true);xhr.upload.onprogress = function(e) {// 更新进度条
};xhr.onload = function() {if (xhr.status === 200) {console.log('上传成功');}
};xhr.send(formData);

     五、事件处理模型

事件触发时机
onreadystatechangereadyState 变化时触发
onloadstart请求开始时触发
onprogress数据传输过程中定期触发
onabort请求被中止时触发
onerror请求失败时触发
onload请求成功完成时触发
ontimeout请求超时时触发
onloadend请求结束时触发(无论成功或失败)
xhr.addEventListener('load', function() {console.log('请求完成');
});xhr.addEventListener('error', function() {console.error('请求出错');
});xhr.addEventListener('abort', function() {console.warn('请求被取消');
});

     六、与 Fetch API 的对比

特性XMLHttpRequestFetch API
基本架构基于事件回调基于 Promise
请求取消原生支持 (xhr.abort())需使用 AbortController
进度监控原生支持 (upload/onprogress)实验性支持或需自定义实现
超时控制原生支持 (timeout 属性)需手动实现
响应类型多种 (text, arraybuffer, blob 等)类似,但通过不同方法访问
CORS 处理需要额外处理更简洁的 CORS 支持
同步请求支持 (open 第三个参数设为 false)不支持
浏览器支持所有现代浏览器 + IE7+现代浏览器 (IE 不支持)
请求体格式支持多种 (FormData, Blob, 字符串)类似

     七、跨域请求处理 (CORS)

         1. 简单请求

xhr.open('GET', 'https://cross-origin.com/data', true);
xhr.send();

         2. 预检请求 (Preflight)

xhr.open('PUT', 'https://cross-origin.com/update', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send(JSON.stringify({ data: 'test' }));

         3. 带凭证的请求

xhr.withCredentials = true; // 发送 cookies

     八、最佳实践与错误处理

         1. 封装成 Promise

function xhrRequest(method, url, data = null) {return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();xhr.open(method, url, true);xhr.setRequestHeader('Content-Type', 'application/json');xhr.onload = function() {if (xhr.status >= 200 && xhr.status < 300) {try {resolve(JSON.parse(xhr.responseText));} catch (e) {resolve(xhr.responseText);}} else {reject(new Error(`请求失败: ${xhr.status} ${xhr.statusText}`));}};xhr.onerror = () => reject(new Error('网络错误'));xhr.ontimeout = () => reject(new Error('请求超时'));xhr.send(data ? JSON.stringify(data) : null);});
}// 使用示例
xhrRequest('GET', '/api/data').then(data => console.log(data)).catch(error => console.error(error));

         2. 统一错误处理

xhr.onreadystatechange = function() {if (xhr.readyState === 4) {switch (true) {case xhr.status === 0:console.error('请求被取消或网络错误');break;case xhr.status >= 200 && xhr.status < 300:handleSuccess(xhr.response);break;case xhr.status === 401:console.error('未授权,需要登录');break;case xhr.status === 403:console.error('禁止访问');break;case xhr.status === 404:console.error('资源不存在');break;case xhr.status >= 500:console.error('服务器错误');break;default:console.error(`未知错误: ${xhr.status}`);}}
};

     九、现代应用场景

         1. 大文件分片上传

function uploadFile(file) {const CHUNK_SIZE = 5 * 1024 * 1024; // 5MBconst totalChunks = Math.ceil(file.size / CHUNK_SIZE);for (let i = 0; i < totalChunks; i++) {const start = i * CHUNK_SIZE;const end = Math.min(start + CHUNK_SIZE, file.size);const chunk = file.slice(start, end);const formData = new FormData();formData.append('chunk', chunk);formData.append('chunkIndex', i);formData.append('totalChunks', totalChunks);formData.append('fileName', file.name);const xhr = new XMLHttpRequest();xhr.open('POST', '/upload-chunk', true);xhr.upload.onprogress = function(e) {const chunkProgress = (e.loaded / e.total) * 100;const totalProgress = ((i * CHUNK_SIZE) + e.loaded) / file.size * 100;updateProgress(totalProgress);};xhr.send(formData);}
}

         2. 长轮询 (Long Polling)

function longPoll() {const xhr = new XMLHttpRequest();xhr.open('GET', '/api/updates', true);xhr.onreadystatechange = function() {if (xhr.readyState === 4) {if (xhr.status === 200) {processUpdates(JSON.parse(xhr.responseText));}// 无论成功或失败,重新发起请求setTimeout(longPoll, 1000);}};xhr.send();
}// 启动长轮询
longPoll();

     十、兼容性与 Polyfill

         1. 浏览器支持

  • 所有现代浏览器完全支持
  • Internet Explorer 7+
  • 移动端浏览器全面支持

         2. ActiveXObject (IE6及以下)

let xhr;
if (typeof XMLHttpRequest !== 'undefined') {xhr = new XMLHttpRequest();
} else {try {// 旧版IExhr = new ActiveXObject('Msxml2.XMLHTTP.6.0');} catch (e) {try {xhr = new ActiveXObject('Msxml2.XMLHTTP.3.0');} catch (e) {throw new Error('XMLHttpRequest not supported');}}
}

     十一、安全注意事项

  1. CSRF 防护

    // 添加CSRF令牌
    const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
    xhr.setRequestHeader('X-CSRF-Token', csrfToken);
    
  2. 内容安全策略

    <meta http-equiv="Content-Security-Policy" content="default-src 'self';">
    
  3. 输入验证

    // 验证服务器响应
    if (xhr.responseType === 'document') {const doc = xhr.responseXML;// 防止XXE攻击if (doc.documentElement.nodeName === 'parsererror') {throw new Error('无效的XML响应');}
    }
    

     总结

XMLHttpRequest 的核心优势:

  1. 广泛的浏览器支持(包括旧版IE)
  2. 完善的进度监控(上传/下载)
  3. 请求取消和超时的原生支持
  4. 同步请求能力(特定场景需要)
  5. 成熟的错误处理机制

虽然 Fetch API 提供了更现代的 Promise-based 接口,但 XHR 在以下场景仍不可替代:

  • 需要精细控制上传/下载进度
  • 兼容旧版浏览器
  • 需要同步请求的特殊场景
  • 现有项目中使用 XHR 的代码库

理解 XMLHttpRequest 的工作原理和高级用法,对于处理复杂网络请求、维护遗留系统和理解 AJAX 技术演进都至关重要。在现代开发中,可根据具体需求选择 XHR 或 Fetch API,甚至结合两者优势使用。

















XMLHttpRequest 与 fetch

XMLHttpRequest 与 Fetch API:全面比较与深度解析

在现代 Web 开发中,XMLHttpRequest (XHR) 和 Fetch API 是两种主要的 HTTP 请求技术。本文将深入比较它们的差异、优缺点和适用场景。


     一、核心架构差异

特性XMLHttpRequest (XHR)Fetch API
设计范式基于事件回调基于 Promise
创建时间1999年 (IE5)2015年 (ES6)
标准类型浏览器厂商实现W3C 标准
基本用法const xhr = new XMLHttpRequest()fetch(url, options)

     二、功能特性对比

         1. 请求与响应处理

// XHR 示例
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/data');
xhr.onload = () => {if (xhr.status === 200) {console.log(JSON.parse(xhr.responseText));}
};
xhr.send();// Fetch 示例
fetch('/api/data').then(response => {if (response.ok) return response.json();throw new Error('请求失败');}).then(data => console.log(data));

         2. 进度监控能力

// XHR - 原生支持进度监控
xhr.upload.onprogress = e => {const percent = Math.round((e.loaded / e.total) * 100);console.log(`上传进度: ${percent}%`);
};// Fetch - 需要额外实现
const response = await fetch(url, { method: 'POST',body: readableStreamWithProgress(file) // 自定义进度流
});

         3. 超时与取消控制

// XHR - 原生支持
xhr.timeout = 5000; // 5秒超时
xhr.ontimeout = () => console.log('请求超时');
xhr.abort(); // 取消请求// Fetch - 需要 AbortController
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000);fetch(url, {signal: controller.signal
}).catch(e => {if (e.name === 'AbortError') console.log('请求取消');
});

     三、关键技术差异详解

         1. 错误处理机制

// XHR 需要手动检查状态码
xhr.onreadystatechange = function() {if (xhr.readyState === 4) {if (xhr.status >= 200 && xhr.status < 300) {// 成功处理} else {// 错误处理}}
};// Fetch 不会拒绝HTTP错误(如404)
fetch(url).then(response => {if (!response.ok) { // 必须手动检查throw new Error(`HTTP错误! 状态码: ${response.status}`);}return response.json();}).catch(error => {// 捕获网络错误和手动抛出的错误});

         2. 请求体与响应体处理

数据类型XHR 支持Fetch 支持
JSONxhr.responseTextresponse.json()
文本xhr.responseTextresponse.text()
Blobxhr.responseresponse.blob()
ArrayBufferxhr.responseresponse.arrayBuffer()
FormData原生支持原生支持
流数据有限支持原生支持(ReadableStream)

         3. CORS 与凭证管理

// XHR
xhr.withCredentials = true; // 发送凭据// Fetch
fetch(url, {credentials: 'include' // 包含凭据
});

     四、性能与能力对比

能力维度XHRFetch
同步请求✅ 支持❌ 不支持
进度事件✅ 原生支持⚠️ 需要自定义实现
超时控制✅ 原生支持⚠️ 需要 AbortController + setTimeout
请求取消xhr.abort()AbortController.abort()
流式处理❌ 有限支持✅ 原生支持(ReadableStream)
服务端事件(SSE)✅ 支持❌ 不支持
HTTP/2 支持✅ 完全支持✅ 完全支持

     五、实际应用场景对比

         1. 文件上传(带进度条)

// XHR 实现 (推荐)
const xhr = new XMLHttpRequest();
xhr.upload.onprogress = e => updateProgress(e);
xhr.open('POST', '/upload');
xhr.send(formData);// Fetch 实现(复杂)
// 需要创建自定义可读流实现进度跟踪

         2. 大数据流处理

// XHR 有限支持
xhr.responseType = 'arraybuffer';// Fetch 原生流支持(推荐)
fetch('/large-data').then(response => {const reader = response.body.getReader();// 流式处理数据});

         3. 简单 API 请求

// Fetch 更简洁(推荐)
fetch('/api/data').then(res => res.json()).then(data => console.log(data));// XHR 实现
const xhr = new XMLHttpRequest();
// ...需要多行代码实现相同功能

     六、浏览器兼容性分析

浏览器XHR 支持Fetch 支持
Chrome✅ 全部版本✅ 42+
Firefox✅ 全部版本✅ 39+
Safari✅ 全部版本✅ 10.1+
Edge✅ 全部版本✅ 14+
Internet Explorer✅ 7+❌ 不支持
Node.js❌ 不支持✅ 18+ (实验性)

Polyfill 方案:

<!-- XHR for IE5-6 -->
<script>// 使用 ActiveXObject 实现
</script><!-- Fetch polyfill -->
<script src="https://cdn.jsdelivr.net/npm/whatwg-fetch@3.6.2/dist/fetch.umd.min.js"></script>

     七、最佳实践指南

         1. 何时选择 XHR

  • 需要精确的上传/下载进度监控
  • 兼容旧版浏览器(特别是 IE9-11)
  • 实现服务器发送事件(SSE)
  • 需要同步请求的特殊场景

         2. 何时选择 Fetch

  • 现代浏览器环境开发
  • 需要流式数据处理
  • 集成 Service Workers
  • 需要更简洁的 Promise 链式调用
  • 使用 HTTP/2 高级特性

         3. 混合使用策略

// 根据需求选择最佳方案
function httpRequest(url, options = {}) {if (options.needProgress && 'XMLHttpRequest' in window) {return xhrRequest(url, options); // 使用XHR}if ('fetch' in window) {return fetchRequest(url, options); // 使用Fetch}// 降级方案return legacyXhrRequest(url, options);
}

     八、未来发展趋势

  1. Fetch 的持续增强

    • 规范中添加了 priority 属性控制请求优先级
    fetch(url, { priority: 'high' }); // 实验性功能
    
  2. HTTP/3 支持

    • Fetch API 更易适配新协议
  3. WebTransport 协议

    // 下一代传输协议
    const transport = new WebTransport('https://example.com');
    const stream = await transport.createBidirectionalStream();
    
  4. Service Worker 集成

    • Fetch 在 PWA 中无缝集成
    self.addEventListener('fetch', event => {event.respondWith(caches.match(event.request) || fetch(event.request));
    });
    

     总结:核心选择建议

考量因素推荐选择
现代浏览器项目✅ Fetch (首选)
需要进度监控✅ XHR
兼容旧版浏览器✅ XHR
流数据处理✅ Fetch
简单API调用✅ Fetch
大文件上传✅ XHR
服务端事件(SSE)✅ XHR
PWA/Service Worker✅ Fetch

最终决策指南:

  1. 优先使用 Fetch API 满足大多数现代 Web 应用需求
  2. 在需要精确进度控制或兼容旧浏览器时使用 XHR
  3. 大型项目可封装统一 HTTP 客户端,内部智能选择最佳实现
  4. 关注 Fetch 规范发展,未来功能将持续增强

两者各有优势,理解其核心差异和适用场景,将帮助您为每个具体需求选择最佳解决方案。

















XMLHttpRequestresponseFetchResponse

XMLHttpRequest 的 response 与 Fetch 的 Response:深度对比

在 AJAX 请求中,XMLHttpRequest (XHR) 和 Fetch API 处理响应数据的方式有显著差异。以下是两者响应处理的全面对比:


     一、核心差异概览

特性XMLHttpRequestFetch API
响应对象XHR 实例本身专用 Response 对象
数据访问通过属性直接访问通过解析方法获取
响应类型设置请求前设置 responseType请求后调用解析方法
HTTP 错误处理需手动检查 status需手动检查 response.ok
流式处理有限支持原生支持 ReadableStream
数据复用可多次访问响应属性响应体只能读取一次

     二、响应数据访问方式对比

         1. XMLHttpRequest 的响应访问

const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/data');
xhr.responseType = 'json'; // 预先设置响应类型xhr.onload = function() {// 通过属性直接访问数据if (xhr.status === 200) {console.log('状态:', xhr.status);console.log('响应文本:', xhr.responseText); // 文本形式console.log('解析后数据:', xhr.response);    // 根据responseTypeconsole.log('响应头:', xhr.getResponseHeader('Content-Type'));}
};
xhr.send();

         2. Fetch 的响应访问

fetch('/api/data').then(response => {console.log('状态:', response.status);console.log('响应头:', response.headers.get('Content-Type'));// 必须调用解析方法获取数据return response.json(); // 返回新Promise}).then(data => {console.log('解析后数据:', data);});

     三、关键差异详解

         1. 响应数据类型处理

数据类型XMLHttpRequestFetch API
文本xhr.responseTextresponse.text()
JSONxhr.response (需设 responseType='json')response.json()
Blobxhr.response (需设 responseType='blob')response.blob()
ArrayBufferxhr.response (需设 responseType='arraybuffer')response.arrayBuffer()
文档xhr.responseXML无直接等效,需 response.text() 后解析
FormData无原生支持response.formData()

         2. 响应头访问对比

// XMLHttpRequest
const contentType = xhr.getResponseHeader('Content-Type');
const allHeaders = xhr.getAllResponseHeaders();// Fetch API
const contentType = response.headers.get('Content-Type');
const headersObj = Object.fromEntries(response.headers.entries());

         3. HTTP 错误处理差异

// XMLHttpRequest
xhr.onreadystatechange = function() {if (xhr.readyState === 4) {if (xhr.status >= 200 && xhr.status < 300) {// 成功处理} else {// HTTP错误处理}}
};// Fetch API
fetch(url).then(response => {if (!response.ok) { // 手动检查throw new Error(`HTTP错误! 状态码: ${response.status}`);}return response.json();}).catch(error => {// 捕获网络错误和手动抛出的错误});

         4. 数据复用能力

// XMLHttpRequest - 可多次访问
console.log(xhr.responseText); // 无限制
console.log(xhr.response);// Fetch API - 响应体只能读取一次
fetch(url).then(response => {const dataPromise = response.json(); // 首次读取// 尝试再次读取会报错response.text() // TypeError: Already read.catch(e => console.error(e));});

         5. 流式数据处理

// XMLHttpRequest - 有限支持
xhr.responseType = 'arraybuffer';
xhr.onprogress = function(e) {if (e.lengthComputable) {const partialData = new Uint8Array(xhr.response);}
};// Fetch API - 原生流支持
fetch('/large-file').then(response => {const reader = response.body.getReader();function processStream({ done, value }) {if (done) return;console.log('收到数据块:', value);return reader.read().then(processStream);}return reader.read().then(processStream);});

     四、特殊场景处理

         1. 二进制数据处理

// XMLHttpRequest
xhr.responseType = 'arraybuffer';
xhr.onload = function() {const buffer = xhr.response;const dataView = new DataView(buffer);
};// Fetch API
fetch('/binary-data').then(response => response.arrayBuffer()).then(buffer => {const dataView = new DataView(buffer);});

         2. 超大型文件处理

// Fetch 流式处理更高效 (内存占用恒定)
async function processLargeFile(url) {const response = await fetch(url);const reader = response.body.getReader();let receivedLength = 0;while (true) {const { done, value } = await reader.read();if (done) break;// 处理数据块receivedLength += value.length;console.log(`已接收 ${receivedLength} 字节`);}
}

         3. 服务端发送事件 (SSE)

// XMLHttpRequest 支持
const xhr = new XMLHttpRequest();
xhr.open('GET', '/sse-endpoint');
xhr.onprogress = function() {console.log('收到部分数据:', xhr.responseText);
};// Fetch API 不支持原生 SSE
// 需使用专用 EventSource API
const es = new EventSource('/sse-endpoint');
es.onmessage = event => {console.log('收到事件:', event.data);
};

     五、性能与内存对比

指标XMLHttpRequestFetch API
内存占用整个响应存储在内存中支持流式处理,内存占用更低
大文件处理可能内存溢出适合处理超大文件
响应延迟需等待整个响应完成可流式处理首批数据
GC 效率整个响应对象统一回收分块数据可及时回收

     六、最佳实践指南

         1. 选择建议:

  • 使用 XMLHttpRequest 当

    • 需要精确的上传/下载进度
    • 兼容 IE11 等旧浏览器
    • 处理小到中型数据
    • 需要同步请求(特殊场景)
  • 使用 Fetch API 当

    • 处理大型文件或流数据
    • 现代浏览器环境
    • 需要更好的错误处理链
    • 集成 Service Worker

         2. 混合使用方案:

async function smartFetch(url, options = {}) {// 需要进度监控时使用 XHRif (options.trackProgress) {return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();xhr.open(options.method || 'GET', url);// 进度处理if (options.onProgress) {xhr.upload.onprogress = options.onProgress;}xhr.onload = () => resolve({status: xhr.status,data: xhr.response,headers: xhr.getAllResponseHeaders()});xhr.onerror = reject;xhr.send(options.body);});}// 默认使用 Fetchtry {const response = await fetch(url, options);const data = await response.json();return { status: response.status, data };} catch (error) {throw error;}
}

     七、未来发展趋势

  1. Fetch 的增强功能

    // 实验性优先级控制
    fetch(url, { priority: 'high' });// 请求诊断API
    const timing = performance.getEntriesByName(url)[0];
    console.log('TTFB:', timing.responseStart - timing.requestStart);
    
  2. WebTransport API

    // 下一代HTTP/3传输协议
    const transport = new WebTransport('https://example.com');
    const stream = await transport.createBidirectionalStream();
    
  3. 响应缓存优化

    fetch(url, {cache: 'force-cache', // 强缓存integrity: 'sha256-...' // 子资源完整性校验
    });
    

     总结:核心选择建议

  • 响应数据访问

    • XHR:直接访问响应属性,更简单
    • Fetch:需要调用解析方法,更现代
  • 大文件处理

    • XHR:有限支持,内存占用高
    • Fetch:原生流支持,内存效率高
  • 错误处理

    • XHR:需手动检查状态码
    • Fetch:需检查 response.ok
  • 浏览器支持

    • XHR:全平台支持(包括 IE7+)
    • Fetch:现代浏览器(IE 不支持)

根据具体需求选择:

  • 简单请求/旧浏览器 → XMLHttpRequest
  • 流处理/现代浏览器 → Fetch API
  • 混合环境 → 使用抽象层封装

理解这些核心差异将帮助您为特定场景选择最合适的 HTTP 请求技术。

















fetch 用例

Fetch API 实用用例大全:从基础到高级实践

Fetch API 是现代 Web 开发的基石,以下是 10 个关键场景的实用代码示例,涵盖从基础请求到高级应用:

     一、基础请求场景

         1. GET 请求 - 获取 JSON 数据

// 获取用户数据
async function getUsers() {try {const response = await fetch('https://api.example.com/users');if (!response.ok) {throw new Error(`HTTP 错误! 状态码: ${response.status}`);}const users = await response.json();console.log('用户列表:', users);return users;} catch (error) {console.error('获取用户失败:', error);return [];}
}

         2. POST 请求 - 提交表单数据

// 创建新用户
async function createUser(userData) {try {const response = await fetch('https://api.example.com/users', {method: 'POST',headers: {'Content-Type': 'application/json','Authorization': 'Bearer YOUR_TOKEN'},body: JSON.stringify(userData),credentials: 'include' // 包含 cookies});if (response.status === 201) {const newUser = await response.json();console.log('用户创建成功:', newUser);return newUser;} else {const errorData = await response.json();throw new Error(errorData.message || '创建用户失败');}} catch (error) {console.error('创建用户错误:', error);throw error;}
}// 使用示例
createUser({ name: 'Alice', email: 'alice@example.com' });

     二、文件处理场景

         3. 上传文件

async function uploadFile(file) {const formData = new FormData();formData.append('file', file);formData.append('description', '用户头像');try {const response = await fetch('/api/upload', {method: 'POST',body: formData,// 不需要手动设置 Content-Type,浏览器会自动设置 multipart/form-data});if (!response.ok) {throw new Error('文件上传失败');}const result = await response.json();console.log('文件上传成功:', result.fileUrl);return result.fileUrl;} catch (error) {console.error('上传错误:', error);throw error;}
}// 使用示例
const fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', async (e) => {if (e.target.files[0]) {await uploadFile(e.target.files[0]);}
});

         4. 下载文件

async function downloadFile(fileUrl, fileName) {try {const response = await fetch(fileUrl);if (!response.ok) {throw new Error('文件下载失败');}const blob = await response.blob();const url = window.URL.createObjectURL(blob);// 创建临时链接并触发下载const a = document.createElement('a');a.href = url;a.download = fileName || 'downloaded-file';document.body.appendChild(a);a.click();// 清理setTimeout(() => {window.URL.revokeObjectURL(url);document.body.removeChild(a);}, 100);} catch (error) {console.error('下载错误:', error);alert('文件下载失败');}
}// 使用示例
downloadFile('https://example.com/report.pdf', '季度报告.pdf');

     三、高级控制场景

         5. 请求超时处理

async function fetchWithTimeout(url, options = {}, timeout = 8000) {const controller = new AbortController();const timeoutId = setTimeout(() => controller.abort(), timeout);try {const response = await fetch(url, {...options,signal: controller.signal});clearTimeout(timeoutId);if (!response.ok) {throw new Error(`请求失败: ${response.status}`);}return response;} catch (error) {if (error.name === 'AbortError') {throw new Error(`请求超时 (${timeout}ms)`);}throw error;}
}// 使用示例
fetchWithTimeout('https://slow-api.com/data', {}, 5000).then(response => response.json()).then(data => console.log(data)).catch(error => console.error('错误:', error.message));

         6. 并发请求处理

async function fetchMultiple(urls) {try {const requests = urls.map(url => fetch(url).then(response => {if (!response.ok) throw new Error(`请求失败: ${url}`);return response.json();}));const results = await Promise.allSettled(requests);// 处理结果const data = [];const errors = [];results.forEach(result => {if (result.status === 'fulfilled') {data.push(result.value);} else {errors.push(result.reason.message);}});console.log('成功请求:', data.length);console.log('失败请求:', errors);return { data, errors };} catch (error) {console.error('并发请求错误:', error);return { data: [], errors: [error.message] };}
}// 使用示例
fetchMultiple(['https://api.example.com/users','https://api.example.com/posts','https://api.example.com/comments'
]);

     四、特殊场景处理

         7. 处理流式数据(大文件/实时数据)

async function processLargeFile(url, onChunk, onComplete) {try {const response = await fetch(url);if (!response.ok) {throw new Error('无法获取文件');}const reader = response.body.getReader();const decoder = new TextDecoder();let receivedLength = 0;while (true) {const { done, value } = await reader.read();if (done) {onComplete();break;}receivedLength += value.length;const chunk = decoder.decode(value, { stream: true });// 处理数据块onChunk(chunk, receivedLength);}} catch (error) {console.error('流处理错误:', error);}
}// 使用示例
processLargeFile('https://example.com/large-log.txt',(chunk, received) => {console.log(`已接收 ${received} 字节`);// 实时处理数据块},() => console.log('文件处理完成')
);

         8. 带进度的文件上传

async function uploadWithProgress(file, onProgress) {const controller = new AbortController();try {const response = await fetch('/api/upload', {method: 'POST',headers: {'X-File-Name': encodeURIComponent(file.name),'X-File-Size': file.size},body: file,signal: controller.signal,duplex: 'half' // 必需参数});if (!response.ok) {throw new Error('上传失败');}return await response.json();} catch (error) {if (error.name !== 'AbortError') {console.error('上传错误:', error);}throw error;}
}// 创建带进度监控的可读流
function trackUploadProgress(file, onProgress) {let uploaded = 0;const total = file.size;const trackedFile = new ReadableStream({start(controller) {const reader = new FileReader();reader.onload = function() {controller.enqueue(new Uint8Array(reader.result));uploaded += reader.result.byteLength;onProgress(uploaded, total);controller.close();};const chunkSize = 1024 * 1024; // 1MB 分块let offset = 0;function readNextChunk() {const chunk = file.slice(offset, offset + chunkSize);reader.readAsArrayBuffer(chunk);offset += chunkSize;if (offset >= file.size) {controller.close();}}readNextChunk();}});return new File([trackedFile], file.name, {type: file.type,lastModified: file.lastModified});
}// 使用示例
const fileInput = document.getElementById('fileInput');
fileInput.addEventListener('change', async (e) => {const file = e.target.files[0];if (file) {const trackedFile = trackUploadProgress(file, (uploaded, total) => {const percent = Math.round((uploaded / total) * 100);console.log(`上传进度: ${percent}%`);});await uploadWithProgress(trackedFile);console.log('文件上传完成');}
});

     五、安全与优化场景

         9. 带重试机制的请求

async function fetchWithRetry(url, options = {}, maxRetries = 3, delay = 1000) {for (let attempt = 1; attempt <= maxRetries; attempt++) {try {const response = await fetch(url, options);// 服务器错误时重试if (response.status >= 500 && attempt < maxRetries) {throw new Error(`服务器错误: ${response.status}`);}if (!response.ok) {throw new Error(`请求失败: ${response.status}`);}return response;} catch (error) {if (attempt === maxRetries) throw error;// 指数退避算法const waitTime = delay * Math.pow(2, attempt - 1);console.warn(`请求失败,${waitTime}ms后重试 (${attempt}/${maxRetries})`);await new Promise(resolve => setTimeout(resolve, waitTime));}}
}// 使用示例
fetchWithRetry('https://unstable-api.com/data', {}, 5).then(response => response.json()).then(data => console.log('最终数据:', data)).catch(error => console.error('所有重试失败:', error));

         10. 请求拦截器(全局处理)

// 保存原始 fetch 引用
const originalFetch = window.fetch;// 自定义 fetch 实现
window.fetch = async function(url, options = {}) {// 1. 请求前处理console.log(`请求发起: ${url}`);// 自动添加认证令牌if (!options.headers) options.headers = {};if (!options.headers['Authorization']) {const token = localStorage.getItem('authToken');if (token) {options.headers['Authorization'] = `Bearer ${token}`;}}// 添加请求时间戳options.headers['X-Request-Timestamp'] = Date.now();try {// 2. 发起实际请求const response = await originalFetch(url, options);// 3. 响应后处理console.log(`请求完成: ${url} [${response.status}]`);// 自动刷新令牌if (response.status === 401) {const refreshed = await refreshToken();if (refreshed) {// 使用新令牌重试请求return window.fetch(url, options);}}return response;} catch (error) {// 4. 错误处理console.error(`请求失败: ${url}`, error);// 全局错误通知showNotification(`网络错误: ${error.message}`);throw error;}
};// 使用示例
// 所有 fetch 请求将自动通过此拦截器
fetch('https://api.example.com/data').then(response => response.json()).then(data => console.log(data));// 辅助函数
async function refreshToken() {const refreshToken = localStorage.getItem('refreshToken');if (!refreshToken) return false;try {const response = await originalFetch('/api/refresh-token', {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({ refreshToken })});if (response.ok) {const { accessToken } = await response.json();localStorage.setItem('authToken', accessToken);return true;}} catch (error) {console.error('令牌刷新失败:', error);}// 刷新失败,清除用户状态localStorage.removeItem('authToken');localStorage.removeItem('refreshToken');return false;
}

     六、实用封装函数

         11. 通用请求封装

class ApiClient {constructor(baseUrl, defaultOptions = {}) {this.baseUrl = baseUrl;this.defaultOptions = {headers: {'Content-Type': 'application/json','Accept': 'application/json'},...defaultOptions};}async request(endpoint, options = {}) {const url = `${this.baseUrl}${endpoint}`;const config = {...this.defaultOptions,...options,headers: {...this.defaultOptions.headers,...(options.headers || {})}};if (config.body && typeof config.body === 'object' && config.headers['Content-Type'] === 'application/json') {config.body = JSON.stringify(config.body);}try {const response = await fetch(url, config);// 处理非200响应if (!response.ok) {let errorData;try {errorData = await response.json();} catch {errorData = { message: `HTTP错误 ${response.status}` };}throw new ApiError(response.status, errorData);}// 处理空响应if (response.status === 204) {return null;}// 根据内容类型解析响应const contentType = response.headers.get('content-type') || '';if (contentType.includes('application/json')) {return await response.json();} else if (contentType.includes('text/')) {return await response.text();} else {return await response.blob();}} catch (error) {if (error instanceof ApiError) throw error;throw new NetworkError(error.message);}}get(endpoint, options = {}) {return this.request(endpoint, { ...options, method: 'GET' });}post(endpoint, body, options = {}) {return this.request(endpoint, { ...options, method: 'POST', body });}put(endpoint, body, options = {}) {return this.request(endpoint, { ...options, method: 'PUT', body });}delete(endpoint, options = {}) {return this.request(endpoint, { ...options, method: 'DELETE' });}
}// 自定义错误类
class ApiError extends Error {constructor(status, data) {super(data.message || `API请求失败: ${status}`);this.name = 'ApiError';this.status = status;this.data = data;}
}class NetworkError extends Error {constructor(message) {super(message || '网络错误');this.name = 'NetworkError';}
}// 使用示例
const api = new ApiClient('https://api.example.com', {credentials: 'include'
});// 获取用户
api.get('/users/123').then(user => console.log('用户:', user)).catch(error => {if (error instanceof ApiError) {console.error(`API错误 [${error.status}]:`, error.data);} else {console.error('网络错误:', error);}});// 创建新项目
api.post('/projects', { title: '新项目', description: '项目描述' }).then(project => console.log('创建的项目:', project));

     关键要点总结

  1. 基础请求:使用 fetch().then().catch() 模式处理基本请求
  2. 错误处理:始终检查 response.okresponse.status
  3. 超时控制:使用 AbortController 实现请求超时
  4. 文件处理
    • 上传:使用 FormData 或二进制流
    • 下载:使用 blob() 和对象 URL
  5. 高级场景
    • 流处理:使用 ReadableStream 处理大数据
    • 进度监控:自定义可读流实现
    • 重试机制:指数退避算法提升可靠性
  6. 安全实践
    • 自动添加认证头
    • 令牌刷新机制
    • 全局错误处理
  7. 封装模式:创建可复用的 API 客户端类

这些实用代码片段可直接集成到项目中,覆盖了大多数网络请求场景的最佳实践。根据具体需求调整参数和错误处理逻辑,可以构建健壮高效的网络请求系统。







































http://www.xdnf.cn/news/17604.html

相关文章:

  • CSS优先级、HTTP响应状态码
  • Android的事件分发流程、Kotlin协程、4大组件、Handler机制、架构设计、性能优化、内存泄漏
  • 第4章 程序段的反复执行2while语句P128练习题(题及答案)
  • 智慧农业-无人机视角庄稼倒伏农作物倒伏检测数据集VOC+YOLO格式541张1类别
  • VSCode添加Python、Java注释技巧、模板
  • 疏老师-python训练营-Day40训练和测试的规范写法
  • NumPy性能飞跃秘籍:向量化计算如何提升400倍运算效率?
  • istio笔记03--快速上手多集群mesh
  • 【C语言】深入探索预处理
  • Matlab 基于BP神经网络结合Bagging(BP-Bagging)集成算法的单变量时序预测 (单输入单输出)
  • 带冷端补偿的热电偶采集方案MAX31855
  • Dell PowerEdge: Servers by generation (按代系划分的服务器)
  • 【渲染流水线】[几何阶段]-[图元装配]以UnityURP为例
  • C++2024 年一级
  • Cursor设置
  • 【机器学习深度学习】模型选型:如何根据现有设备选择合适的训练模型
  • 【面试场景题】微博热点新闻系统设计方案
  • 一个“加锁无效“的诡异现象
  • #C语言——刷题攻略:牛客编程入门训练(七):分支控制(一)-- 涉及 %c前加空格:忽略起首的空白字符
  • Spring Boot Starter 自动化配置原理深度剖析
  • 把大模型“关进冰箱”——基于知识蒸馏 + 动态量化的小型化实战笔记
  • 推客系统开发全攻略:从架构设计到高并发实战
  • 【Python 高频 API 速学 ②】
  • 让大模型 “睡觉”:把版本迭代当作人类睡眠来设计(附可直接改造的训练作息表与代码)
  • 【Task2】【Datawhale AI夏令营】多模态RAG
  • Python基础教程(四)字符串和编码:深度探索Python字符串与编码的终极指南
  • Milvus 向量数据库基础操作解析
  • Node.js特训专栏-实战进阶:22. Docker容器化部署
  • 模板方法模式:优雅封装算法骨架
  • 代码随想录day60图论10