ofetch
一个更好的 fetch API。在 Node、浏览器和 Worker 中均可使用。
一个更好的 fetch API。在 Node、浏览器和 Worker 中均可使用。
🚀 快速开始
安装
# npm
npm i ofetch
# yarn
yarn add ofetch
导入
// ESM / Typescript
import { ofetch } from "ofetch";
// CommonJS
const { ofetch } = require("ofetch");
✔️ 兼容 Node.js
我们使用 条件导出 来检测 Node.js 并自动使用 unjs/node-fetch-native。如果 globalThis.fetch
可用,则会优先使用它。要利用 Node.js 17.5.0 的实验性原生 fetch API,请使用 --experimental-fetch
标志。
keepAlive
支持
通过将 FETCH_KEEP_ALIVE
环境变量设置为 true
,将注册一个 HTTP/HTTPS 代理,即使没有待处理的请求,它也会保持套接字连接,以便未来的请求可以直接使用,而无需重新建立 TCP 连接。
注意: 此选项可能导致内存泄漏。请查阅 node-fetch/node-fetch#1325。
✔️ 解析响应
ofetch
将使用 destr 智能解析 JSON 和原生值,如果解析失败则回退到文本。
const { users } = await ofetch("/api/users");
对于二进制内容类型,ofetch
将返回一个 Blob
对象。
您可以选择提供一个不同于 destr
的解析器,或者指定 blob
、arrayBuffer
或 text
以强制使用相应的 FetchResponse
方法解析正文。
// Use JSON.parse
await ofetch("/movie?lang=en", { parseResponse: JSON.parse });
// Return text as is
await ofetch("/movie?lang=en", { parseResponse: (txt) => txt });
// Get the blob version of the response
await ofetch("/api/generate-image", { responseType: "blob" });
✔️ JSON 正文
如果向 body
选项传入一个对象或具有 .toJSON()
方法的类,ofetch
会自动将其字符串化。
ofetch
利用 JSON.stringify()
转换传入的对象。没有 .toJSON()
方法的类必须在传入 body
选项之前提前转换为字符串值。
对于 PUT
、PATCH
和 POST
请求方法,当设置字符串或对象正文时,ofetch
会添加默认的 content-type: "application/json"
和 accept: "application/json"
头部(您可以随时覆盖它们)。
此外,ofetch
支持使用 Buffer
、ReadableStream
、Stream
和 兼容的正文类型 的二进制响应。ofetch 将自动设置 duplex: "half"
选项以支持流式传输!
示例
const { users } = await ofetch("/api/users", {
method: "POST",
body: { some: "json" },
});
✔️ 错误处理
当 response.ok
为 false
时,ofetch
会自动抛出带有友好错误消息和简洁堆栈(隐藏内部细节)的错误。
解析后的错误正文可通过 error.data
获取。您也可以使用 FetchError
类型。
await ofetch("https://google.com/404");
// FetchError: [GET] "https://google/404": 404 Not Found
// at async main (/project/playground.ts:4:3)
捕获错误响应
await ofetch("/url").catch((err) => err.data);
要绕过状态错误捕获,您可以设置 ignoreResponseError
选项
await ofetch("/url", { ignoreResponseError: true });
✔️ 自动重试
如果发生错误且响应状态码包含在 retryStatusCodes
列表中,ofetch
会自动重试请求。
重试状态码
408
- 请求超时409
- 冲突425
- 过早429
- 请求过多500
- 内部服务器错误502
- 错误网关503
- 服务不可用504
- 网关超时
您可以使用 retry
和 retryDelay
选项来指定重试次数和重试之间的延迟,也可以使用 retryStatusCodes
选项传入自定义代码数组。
retry
的默认值为 1
次重试,但对于 POST
、PUT
、PATCH
和 DELETE
方法,ofetch
默认不重试以避免引入副作用。如果您为 retry
设置了自定义值,则对于所有请求都将始终重试。
retryDelay
的默认值为 0
毫秒。
await ofetch("http://google.com/404", {
retry: 3,
retryDelay: 500, // ms
});
✔️ 超时
您可以指定以毫秒为单位的 timeout
,以便在超时后自动中止请求(默认禁用)。
await ofetch("http://google.com/404", {
timeout: 3000, // Timeout after 3 seconds
});
✔️ 类型友好
响应可以获得类型辅助
const article = await ofetch<Article>(`/api/article/${id}`);
// Auto complete working with article.id
✔️ 添加 baseURL
通过使用 baseURL
选项,ofetch
会使用 ufo 自动为其预置斜杠和查询搜索参数。
await ofetch("/config", { baseURL });
✔️ 添加查询搜索参数
通过使用 query
选项(或 params
作为别名),ofetch
会使用 ufo 在保留请求本身查询的情况下,将查询搜索参数添加到 URL 中。
await ofetch("/movie?lang=en", { query: { id: 123 } });
✔️ 拦截器
可以提供异步拦截器来 Hook ofetch
调用的生命周期事件。
您可能希望使用 ofetch.create
来设置共享拦截器。
onRequest({ request, options })
当 ofetch
被调用时,onRequest
会立即被调用,允许您修改选项或进行简单的日志记录。
await ofetch("/api", {
async onRequest({ request, options }) {
// Log request
console.log("[fetch request]", request, options);
// Add `?t=1640125211170` to query search params
options.query = options.query || {};
options.query.t = new Date();
},
});
onRequestError({ request, options, error })
当 fetch 请求失败时,onRequestError
将被调用。
await ofetch("/api", {
async onRequestError({ request, options, error }) {
// Log error
console.log("[fetch request error]", request, error);
},
});
onResponse({ request, options, response })
onResponse
将在 fetch
调用和解析正文后被调用。
await ofetch("/api", {
async onResponse({ request, response, options }) {
// Log response
console.log("[fetch response]", request, response.status, response.body);
},
});
onResponseError({ request, options, response })
onResponseError
与 onResponse
相同,但当 fetch 发生且 response.ok
不为 true
时将被调用。
await ofetch("/api", {
async onResponseError({ request, response, options }) {
// Log error
console.log(
"[fetch response error]",
request,
response.status,
response.body
);
},
});
✔️ 使用默认选项创建 fetch
如果您需要在多个 fetch 调用中使用通用选项,此实用程序非常有用。
注意: 默认值将进行一级克隆并继承。请注意像 headers
这样的嵌套选项。
const apiFetch = ofetch.create({ baseURL: "/api" });
apiFetch("/test"); // Same as ofetch('/test', { baseURL: '/api' })
💡 添加头部
通过使用 headers
选项,ofetch
会在请求的默认头部基础上添加额外的头部
await ofetch("/movies", {
headers: {
Accept: "application/json",
"Cache-Control": "no-cache",
},
});
💡 添加 HTTP(S) Agent
如果您需要使用 HTTP(S) Agent,可以添加 agent
选项并配合 https-proxy-agent
(仅适用于 Node.js)
import { HttpsProxyAgent } from "https-proxy-agent";
await ofetch("/api", {
agent: new HttpsProxyAgent("http://example.com"),
});
🍣 访问原始响应
如果您需要访问原始响应(例如获取头部),可以使用 ofetch.raw
const response = await ofetch.raw("/sushi");
// response._data
// response.headers
// ...
原生 fetch
作为快捷方式,您可以使用 ofetch.native
,它提供了原生的 fetch
API
const json = await ofetch.native("/sushi").then((r) => r.json());
📦 打包器注意事项
- 所有目标都以 Module 和 CommonJS 格式以及命名导出方式导出
- 为了支持现代语法,没有导出被转译
- 您可能需要使用 Babel 转译
ofetch
、destr
和ufo
包以支持 ES5
- 您可能需要使用 Babel 转译
- 您需要为支持旧版浏览器(例如使用 unfetch)填充
fetch
全局变量
❓ 常见问题
为什么导出名为 ofetch
而不是 fetch
?
使用与 fetch
相同的名称可能会引起混淆,因为 API 不同,但它仍然是一个 fetch,所以使用了最接近的替代方案。但是,您可以从 ofetch
中导入 { fetch }
,它会自动为 Node.js 填充,否则使用原生 fetch。
为什么没有默认导出?
默认导出与 CommonJS 导出混合使用总是存在风险。
这也保证了我们可以在不破坏包的情况下引入更多实用工具,并鼓励使用 ofetch
名称。
为什么不转译?
通过转译库,我们正在用遗留代码阻碍 Web 的发展,而这些代码对大多数用户来说是不必要的。
如果您需要支持旧版用户,可以选择在构建流程中转译该库。
许可
MIT。倾情打造 💖