npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

iota-fetch

v0.2.0

Published

一个基于 **axios** 的二次封装请求库,提供统一的 Axios Method 请求调用方式,并内置**请求队列**能力(可按 key 中断单个请求或中断全部请求)。支持自定义 axios 默认配置、请求/响应拦截器,以及可选的 loading 显示/隐藏钩子。

Readme

iota-fetch

一个基于 axios 的二次封装请求库,提供统一的 Axios Method 请求调用方式,并内置请求队列能力(可按 key 中断单个请求或中断全部请求)。支持自定义 axios 默认配置、请求/响应拦截器,以及可选的 loading 显示/隐藏钩子。

GitHub · 问题反馈 · 更新日志

安装

npm i iota-fetch axios

本项目本身依赖 axios,如果你在业务工程里已安装 axios,可按你的依赖管理策略处理(避免重复安装/版本冲突)。

快速开始

import { createFetch } from "iota-fetch";

const fetch = createFetch({
  requestConfig: {
    baseURL: "https://api.example.com",
    headers: {
      "Content-Type": "application/json;charset=utf-8",
    },
  },
});

// GET:第二个参数会作为 query params
const list = await fetch.get("/items", { page: 1, pageSize: 10 });

// POST:第二个参数会作为 body data
const created = await fetch.post("/items", { name: "foo" });

TypeScript 返回值

请求方法支持通过泛型声明响应数据类型。封装会自动返回 response.data,不需要再手动读取 Axios 响应对象:

type Item = {
  id: string;
  name: string;
};

const item = await fetch.get<undefined, Item>("/items/1");
item.id;

请求级配置类型也可以直接从包中导入:

import type { ConfigProps } from "iota-fetch";

const config: ConfigProps = {
  timeout: 5000,
  loading: true,
};

请求级配置

第三个参数支持收缩后的 ConfigProps,会覆盖实例级配置。例如可以单独设置超时或请求头:

const result = await fetch.get("/items", { page: 1 }, {
  timeout: 5000,
  headers: { "X-Trace-Id": "trace-id" },
});

config.loading 是本封装提供的扩展字段,只控制 loading 钩子,不会被发送给 Axios adapter。请求级 params 和 data 会分别由无请求体方法与有请求体方法的第二个参数生成。

API

createFetch(options)

创建一个包含全部 Axios Method 请求方法的请求对象。

参数 options

  • requestConfig: CreateAxiosDefaults
    • axios 实例默认配置(如 baseURL、timeout、headers 等)
  • requestIntercept?: () => { onFulfilled, onRejected }
    • 请求拦截器工厂函数(内部会 instance.interceptors.request.use(...))
  • responseIntercept?: () => { onFulfilled, onRejected }
    • 响应拦截器工厂函数(内部会 instance.interceptors.response.use(...))
  • loading?: { show(): void; hide(): void }
    • 传入后可配合单次请求的 config.loading 控制显示/隐藏
  • cancelRequest?: boolean
    • 是否启用请求取消和同 key 自动取消旧请求,默认值为 true
  • requestQueue?: RequestQueue
    • 可选的共享队列。默认每个 createFetch 实例使用独立队列,只有明确传入同一个队列时才会跨实例取消请求

requestConfig 使用 Axios 的 CreateAxiosDefaults 类型,常用配置包括 baseURL、timeout、headers、withCredentials 和自定义 adapter。拦截器工厂函数会在创建实例时执行一次。

如果需要允许相同请求并发执行,可以关闭请求取消:

const fetch = createFetch({
  requestConfig: { baseURL: "https://api.example.com" },
  cancelRequest: false,
});

关闭后不会注册请求队列,也不会自动取消旧请求;对应实例的 fetch.requestQueue.abortive() 和 fetch.requestQueue.removeResAll() 只会作用于启用 cancelRequest 创建的请求。

返回值

返回 fetch 对象:

  • fetch.get(url, data?, config?)
  • fetch.delete(url, data?, config?)
  • fetch.head(url, data?, config?)
  • fetch.options(url, data?, config?)
  • fetch.post(url, data?, config?)
  • fetch.put(url, data?, config?)
  • fetch.patch(url, data?, config?)
  • fetch.purge(url, data?, config?)
  • fetch.link(url, data?, config?)
  • fetch.unlink(url, data?, config?)
  • fetch.query(url, data?, config?)

其中:

  • url: string
  • data?: unknown
  • config?: ConfigProps

行为规则:

  • get/delete/head/options:data 会被放到 params
  • post/put/patch/purge/link/unlink/query:data 会被放到 data
  • 返回值:默认 Promise<R>,内部返回 response.data

ConfigProps 是收缩后的请求级配置类型,不能覆盖封装管理的 url、method、data、params 和 signal;这些值分别由方法参数、请求方法和请求队列统一控制。

例如:

await fetch.head("/items");
await fetch.options("/items");
await fetch.purge("/items/1", { reason: "expired" });
await fetch.link("/items/1", { related: "/owners/1" });
await fetch.unlink("/items/1", { related: "/owners/1" });
await fetch.query("/items", { filter: { active: true } });

请求失败时会保留 Axios 原始错误对象,调用方可以使用 axios.isAxiosError(error) 或检查 error.response、error.code:

try {
  const result = await fetch.get<undefined, Item[]>("/items");
} catch (error) {
  if (axios.isAxiosError(error)) {
    console.error(error.response?.status, error.message);
  }
}

如果使用上面的错误处理示例,请额外导入 axios:

import axios from "axios";

拦截器示例

import type { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from "axios";
import { createFetch } from "iota-fetch";

const fetch = createFetch({
  requestConfig: { baseURL: "https://api.example.com" },
  requestIntercept: () => ({
    onFulfilled: (config: InternalAxiosRequestConfig) => {
      config.headers.set("X-Token", "your-token");
      return config;
    },
    onRejected: (error: AxiosError) => Promise.reject(error),
  }),
  responseIntercept: () => ({
    onFulfilled: (resp: AxiosResponse) => resp,
    onRejected: (error: AxiosError) => Promise.reject(error),
  }),
});

Loading 示例

import { createFetch } from "iota-fetch";

const loading = {
  show: () => console.log("loading show"),
  hide: () => console.log("loading hide"),
};

const fetch = createFetch({
  requestConfig: { baseURL: "https://api.example.com" },
  loading,
});

// 只有当 config.loading 为 true 才会触发 show/hide
await fetch.get("/items", { page: 1 }, { loading: true });

Loading 并发计数建议

封装层会为每个启用 config.loading 的请求调用一次 show(),并在请求成功、失败或取消后调用一次 hide()。建议在 loading 组件内部维护并发计数:只有第一个请求开始时显示 loading,所有请求结束后再隐藏 loading。

let activeRequests = 0;

const loading = {
  show() {
    activeRequests += 1;
    if (activeRequests === 1) {
      openLoading();
    }
  },
  hide() {
    activeRequests = Math.max(0, activeRequests - 1);
    if (activeRequests === 0) {
      closeLoading();
    }
  },
};

openLoading 和 closeLoading 替换为业务组件实际的显示/隐藏方法即可。计数逻辑应放在 loading 组件中,不建议在每个请求调用处重复维护;这样多个并发请求、请求失败和请求取消都能保持正确的 loading 状态。

请求队列与取消请求

每个 createFetch 实例都会拥有独立的请求队列,内部用 AbortController 管理请求。可以通过返回对象上的 fetch.requestQueue 获取当前实例队列:

// 获取当前队列快照(ReadonlyMap<key, AbortController>)
const q = fetch.requestQueue.getQueue();
console.log(q.size);

getQueue() 返回的是快照,修改这个 Map 不会改变内部队列。请求完成后会自动移除对应项。

如果业务确实需要多个实例共享取消流程,可以显式传入同一个 RequestQueue:

import { createFetch, RequestQueue } from "iota-fetch";

const sharedQueue = new RequestQueue();
const userFetch = createFetch({ requestConfig: { baseURL: "/user" }, requestQueue: sharedQueue });
const orderFetch = createFetch({ requestConfig: { baseURL: "/order" }, requestQueue: sharedQueue });

包仍然导出 requestQueue 作为独立的共享队列实例,但它不会自动接管新建 fetch 实例的请求。

key 规则

key 默认由以下规则生成:

  1. 基础部分:method + "-" + url
  2. 数据部分:如果存在 data,则对其进行以下处理:
    • 递归排序对象键
    • 清洗数据(过滤 undefined 和函数)
    • 将处理后的数据转换为 JSON 字符串
    • 使用 MD5 生成哈希值
  3. 最终 key:基础部分 + "-" + MD5哈希值

这样处理的好处是:

  • 相同的请求参数(无论顺序如何)会生成相同的 key
  • 避免了复杂参数导致 key 过长的问题
  • 提高了 key 的唯一性和安全性

你也可以手动生成:

const key = fetch.requestQueue.createKey("get", "/items", { page: 1 });

示例:

// 以下两个请求会生成相同的 key
const key1 = fetch.requestQueue.createKey("get", "/items", { page: 1, size: 10 });
const key2 = fetch.requestQueue.createKey("get", "/items", { size: 10, page: 1 });

// 输出示例:"get-/items-5f4dcc3b5aa765d61d8327deb882cf99"

中断单个请求

fetch.requestQueue.abortive(key);

中断所有请求

fetch.requestQueue.removeResAll();

同 key 自动取消旧请求(内置行为)

当 cancelRequest 开启时,发起新请求如果队列里已存在同一个 key,会先取消旧请求再登记新请求(避免相同请求并发造成的覆盖与浪费)。

注意:是否算“同一个请求”取决于 key(包含 method/url/data 的 JSON 字符串)。

开启 cancelRequest 时,同 key 的新请求会自动取消旧请求。若需要保留两个相同 URL 但参数不同的请求,请确保第二个参数不同;也可以通过 fetch.requestQueue.createKey() 获取对应 key 后手动调用 fetch.requestQueue.abortive()。

开发与构建

# 构建产物到 lib/(CJS + ESM + d.ts)
npm run build

# 开发模式(watch + 本地静态服务 + livereload)
npm run start

# 自动发布脚本(检查代码更新、npm 登录状态、版本管理)
npm run release

构建输出(默认):

  • lib/bundle.cjs.js
  • lib/bundle.esm.js
  • lib/main.d.ts

发布流程

  1. 运行 npm run release 启动发布流程
  2. 脚本会检查代码是否有更新
  3. 检查 npm 是否登录
  4. 显示当前版本和仓库版本
  5. 选择版本升级类型(小版本/中版本/大版本)
  6. 执行构建和发布
  7. 显示远程包的最新版本

版本管理

  • 小版本:修复 bug,向后兼容
  • 中版本:添加新功能,向后兼容
  • 大版本:不向后兼容的变更