1. Fetch API 是浏览器原生提供的新一代网络请求接口,用于替代传统的 XMLHttpRequest (XHR)
  2. 它基于 Promise 设计,支持异步 / 同步请求、请求 / 响应拦截、流式处理等特性,语法更简洁、功能更强大,是现代前端开发中网络请求的首选方案;

核心概念

基本定义

  1. Fetch API 提供了一个全局的 fetch() 方法,用于发起网络请求并获取资源;

  2. 它的核心特点:

    1. 基于 Promise,支持 async/await 语法,异步流程更清晰;
    2. 仅在 网络错误 (如断网、跨域限制、服务器无响应)rejectHTTP 错误 (4xx/5xx) 不会触发 reject
    3. 默认不携带 Cookie,需手动配置;
    4. 不支持超时控制,需手动实现;
    5. 支持 Request/Response 对象,可更灵活地配置请求和处理响应;

与 XMLHttpRequest 的区别

特性 Fetch API XMLHttpRequest
语法风格 Promise 化,支持 async/await 回调函数 (回调地狱风险)
错误处理 仅网络错误 reject 状态码错误可通过 status 判断
Cookie 携带 默认不携带,需配置 默认携带
超时控制 无原生支持,需手动实现 原生 timeout 属性
中止请求 支持 (AbortController) 支持 (abort() 方法)
响应处理 需手动解析 (json/text/blob) 自动解析响应体
扩展性 支持 Request/Response 封装 配置繁琐,扩展性差

基本用法

语法结构

  1. 语法结构

    fetch(resource, options)
      .then(response => {
        // 处理响应(需先解析响应体)
        if (!response.ok) {
          throw new Error(`HTTP 错误!状态码:${response.status}`);
        }
        return response.json(); // 解析为 JSON(或 text/blob 等)
      })
      .then(data => {
        // 处理解析后的数据
        console.log('请求成功:', data);
      })
      .catch(error => {
        // 捕获网络错误或自定义错误
        console.error('请求失败:', error);
      });
    
  2. 参数说明:

    1. resource:请求地址 (字符串)Request 对象;
    2. options (可选):请求配置对象,常用属性如下:
    属性 类型 说明 默认值
    method 字符串 请求方法 (GET/POST/PUT/DELETE 等) “GET”
    headers 对象 / Headers 请求头 (如 Content-Type、Authorization) {}
    body 字符串 / Blob / FormData 请求体 (POST/PUT 等方法用) null (GET 无)
    mode 字符串 跨域模式 (cors/no-cors/same-origin) “cors”
    credentials 字符串 Cookie 携带策略 (include/same-origin/omit) “same-origin”
    cache 字符串 缓存策略 (default/no-store/reload 等) “default”
    signal AbortSignal 用于中止请求 (配合 AbortController) undefined

常见请求示例

// 简洁写法
fetch('https://api.example.com/data')
  .then(res => {
    if (!res.ok) throw new Error('请求失败');
    return res.json(); // 解析 JSON 响应体
  })
  .then(data => console.log(data))
  .catch(err => console.error(err));

// 带查询参数的 GET 请求
const params = new URLSearchParams({ id: 1, name: 'test' });
fetch(`https://api.example.com/data?${params}`)
  .then(res => res.json())
  .then(data => console.log(data));
fetch('https://api.example.com/submit', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json', // 告诉服务器请求体是 JSON 格式
    'Authorization': 'Bearer your-token' // 可选:身份验证令牌
  },
  body: JSON.stringify({ username: 'admin', password: '123456' }), // 序列化 JSON
  credentials: 'include' // 携带跨域 Cookie(如需)
})
.then(res => {
  if (!res.ok) throw new Error(`状态码:${res.status}`);
  return res.json();
})
.then(data => console.log('提交成功:', data))
.catch(err => console.error('提交失败:', err));
// 方式 1:FormData 对象(适合文件上传或表单提交)
const formData = new FormData();
formData.append('username', 'admin');
formData.append('avatar', fileInput.files[0]); // 上传文件

fetch('https://api.example.com/upload', {
  method: 'POST',
  body: formData, // 无需设置 Content-Type,浏览器自动添加 multipart/form-data
  credentials: 'include'
})
.then(res => res.json())
.then(data => console.log('上传成功:', data));


// 方式 2:URLSearchParams(适合 application/x-www-form-urlencoded 格式)
const formParams = new URLSearchParams();
formParams.append('username', 'admin');
formParams.append('password', '123456');

fetch('https://api.example.com/login', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  body: formParams
})
.then(res => res.json())
.then(data => console.log('登录成功:', data));
// PUT 请求(更新数据)
fetch('https://api.example.com/data/1', {
  method: 'PUT',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'updated' })
})
.then(res => res.json())
.then(data => console.log('更新成功:', data));


// DELETE 请求(删除数据)
fetch('https://api.example.com/data/1', { method: 'DELETE' })
.then(res => {
  if (res.ok) console.log('删除成功');
});

Request 对象

  1. 除了使用基本的 fetch 方法,还可以通过创建一个 Request 对象来完成请求(fetch 的内部会创建一个 Request 对象);

    let req = new Request(url[, 配置]);
    fetch(req)
    
  2. 尽量保证每次请求都是一个新的 Request 对象;

    let req;
    
    function getRequestInfo() {
      if (!req) {
        const url = 'http://study.yuanjin.tech/api/local';
        req = new Request(url, {});
        console.log(req);
      }
      return req.clone(); // 克隆一个全新的 request 对象,配置一致
    }
    
    async function getProvinces() {
      const resp = await fetch(getRequestInfo());
      const result = await resp.json();
      console.log(result);
    }
    
    document.querySelector('button').onclick = function () {
      getProvinces();
    };
    

Headers 对象

  1. RequestResponse 对象内部,会将传递的请求头对象转换为 Headers

  2. Headers 对象中的方法

    方法 描述
    has(key) 检查请求头中是否存在指定的 key
    get(key) 得到请求头中对应的 key
    set(key, value) 修改对应的键值对
    append(key, value) 添加对应的键值对
    keys() 得到所有的请求头键的集合
    values() 得到所有的请求头中的值的集合
    entries() 得到所有请求头中的键值对的集合
  3. 示例代码

    let req;
    
    function getCommonHeaders() {
      return new Headers({
        a: 1,
        b: 2,
      });
    }
    
    function getRequestInfo() {
      if (!req) {
        req = new Request('http://study.yuanjin.tech/api/local', { headers: getCommonHeaders() });
      }
      return req.clone(); // 克隆一个全新的 request 对象,配置一致
    }
    
    async function getProvinces() {
      const resp = await fetch(getRequestInfo());
      const result = await resp.json();
      console.log(result);
    }
    
    document.querySelector('button').onclick = function () {
      getProvinces();
    };
    

响应处理

fetch() 成功后返回的 response 对象是一个 Response 实例,包含响应状态、响应头、响应体等信息,但响应体需通过专门的方法解析;

Response 核心属性

属性 说明
ok 布尔值,status200-299 之间时为 true
status HTTP 状态码 (如 200、404、500)
statusText 状态文本 (如 “OK”、“Not Found”)
headers 响应头 (Headers 对象,支持 get() 方法获取)
url 最终响应的 URL (可能因重定向变化)
redirected 是否发生重定向

响应体解析方法(均返回 Promise)

  1. 根据响应数据类型选择对应的解析方法:

    方法 说明 适用场景
    response.json() 解析为 JSON 对象 接口返回 JSON 数据 (最常用)
    response.text() 解析为字符串 纯文本、HTML
    response.blob() 解析为 Blob 对象 (二进制数据) 图片、音频、视频等文件
    response.formData() 解析为 FormData 对象 表单提交后的响应
    response.arrayBuffer() 解析为 ArrayBuffer (二进制缓冲区) 处理二进制数据 (如加密)
  2. 示例:下载图片并显示

    fetch('https://api.example.com/image.jpg')
      .then(res => {
        if (!res.ok) throw new Error('图片下载失败');
        return res.blob(); // 解析为 Blob 对象
      })
      .then(blob => {
        const imgUrl = URL.createObjectURL(blob); // 创建临时 URL
        const img = document.createElement('img');
        img.src = imgUrl;
        document.body.appendChild(img);
      })
      .catch(err => console.error(err));
    

高级特性

超时控制

Fetch 无原生超时配置,需通过 Promise.race() / AbortController 实现 (超时后中止请求)

function fetchWithTimeout(resource, options = {}, timeout = 5000) {
  // 创建中止控制器
  const controller = new AbortController();
  const { signal } = controller;

  // 超时后中止请求
  const timeoutId = setTimeout(() => controller.abort(), timeout);

  // 合并 signal 到 options
  const fetchOptions = { ...options, signal };

  return fetch(resource, fetchOptions)
    .then(res => {
      clearTimeout(timeoutId); // 请求成功,清除超时计时器
      return res;
    })
    .catch(err => {
      clearTimeout(timeoutId);
      // 区分超时错误和其他错误
      if (err.name === 'AbortError') {
        throw new Error(`请求超时(${timeout}ms)`);
      }
      throw err;
    });
}

// 使用示例
fetchWithTimeout('https://api.example.com/data', { method: 'GET' }, 3000)
  .then(res => res.json())
  .then(data => console.log(data))
  .catch(err => console.error(err)); // 超时会抛出 "请求超时(3000ms)"

中止请求

通过 AbortController 可以手动中止正在进行的 Fetch 请求 (如用户取消操作)

const controller = new AbortController();
const { signal } = controller;

// 发起请求
fetch('https://api.example.com/data', { signal })
  .then(res => res.json())
  .then(data => console.log(data))
  .catch(err => {
    if (err.name === 'AbortError') {
      console.log('请求已被中止');
    } else {
      console.error(err);
    }
  });

// 手动中止请求(如点击取消按钮时)
document.getElementById('cancelBtn').addEventListener('click', () => {
  controller.abort();
});

跨域请求(CORS)

  1. Fetch 默认支持跨域请求 (mode: “cors”),但需服务器配置 CORS 响应头 (如 Access-Control-Allow-Origin)

  2. 关键配置:

    1. mode: “cors”:允许跨域请求,服务器需返回 CORS 头;
    2. mode: “no-cors”:仅允许简单跨域请求 (GET/POST/HEAD,无自定义头),响应无法访问响应体;
    3. credentials: “include”:跨域请求时携带 Cookie (需服务器配置 Access-Control-Allow-Credentials: true)
fetch('https://cross-domain-api.example.com/data', {
  method: 'GET',
  credentials: 'include', // 携带跨域 Cookie
  mode: 'cors'
})
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));

请求 / 响应拦截

Fetch 无原生拦截器,但可通过封装函数实现类似 Axios 的拦截效果;

// 拦截器配置
const interceptors = {
  request: (config) => {
    // 请求拦截:添加统一请求头、Token 等
    config.headers = {
      ...config.headers,
      'Authorization': 'Bearer your-token',
      'X-Request-From': 'web'
    };
    return config;
  },
  response: (response) => {
    // 响应拦截:统一处理成功响应
    if (!response.ok) throw new Error(`状态码:${response.status}`);
    return response.json();
  },
  error: (error) => {
    // 错误拦截:统一处理错误(如 Token 过期、网络错误)
    if (error.message.includes('401')) {
      console.log('Token 过期,跳转登录页');
      // window.location.href = '/login';
    }
    return Promise.reject(error);
  }
};

// 封装带拦截器的 fetch
function request(resource, options = {}) {
  const config = interceptors.request({ resource, ...options });
  return fetch(config.resource, config)
    .then(interceptors.response)
    .catch(interceptors.error);
}

// 使用示例
request('https://api.example.com/data', { method: 'GET' })
  .then(data => console.log('请求成功:', data))
  .catch(err => console.error('请求失败:', err));

缓存控制

  1. 通过 options.cache 配置请求的缓存策略 (优先级高于 HTTP 缓存头)

    cache 值 说明
    default 优先使用缓存,无缓存则请求网络
    no-store 不使用缓存,直接请求网络 (不存储响应)
    reload 不使用缓存,直接请求网络 (存储响应)
    no-cache 验证缓存有效性,有效则用缓存,否则请求网络
    force-cache 强制使用缓存 (即使缓存过期),无缓存才请求网络
    only-if-cached 仅使用缓存,无缓存则返回 504 错误
  2. 示例:禁用缓存

    fetch('https://api.example.com/data', { cache: 'no-store' })
      .then(res => res.json())
      .then(data => console.log(data));
    

错误处理最佳实践

  1. Fetch 的错误处理需注意两点:

    1. HTTP 错误 (4xx/5xx) 不会触发 catch,需通过 response.ok 手动判断;
    2. 网络错误 (断网、跨域限制) 会触发 catch,需区分错误类型;
  2. 示例代码

    fetch('https://api.example.com/data')
      .then(res => {
        // 处理 HTTP 错误
        if (!res.ok) {
          return res.json().catch(() => res.text()).then(body => {
            // 携带响应体信息抛出错误(便于调试)
            throw new Error(`HTTP ${res.status}: ${JSON.stringify(body)}`);
          });
        }
        return res.json();
      })
      .then(data => console.log(data))
      .catch(err => {
        // 区分错误类型
        if (err.name === 'AbortError') {
          console.log('请求已中止');
        } else if (err.message.includes('Failed to fetch')) {
          console.log('网络错误(断网/跨域限制)');
        } else {
          console.error('请求失败:', err.message);
        }
      });
    
打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

你好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. 核心概念
    1. 1.1. 基本定义
    2. 1.2. 与 XMLHttpRequest 的区别
  2. 2. 基本用法
    1. 2.1. 语法结构
    2. 2.2. 常见请求示例
  3. 3. Request 对象
  4. 4. Headers 对象
  5. 5. 响应处理
    1. 5.1. Response 核心属性
    2. 5.2. 响应体解析方法(均返回 Promise)
  6. 6. 高级特性
    1. 6.1. 超时控制
    2. 6.2. 中止请求
    3. 6.3. 跨域请求(CORS)
    4. 6.4. 请求 / 响应拦截
    5. 6.5. 缓存控制
  7. 7. 错误处理最佳实践