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

fetch-controlled-image

v1.0.0

Published

A React component that controls image loading via fetch with AbortController, avoiding browser concurrent request limits and supporting graceful fallback

Readme

FetchControlledImage

一个使用 fetch + AbortController 控制图片加载的 React 组件。

特性

  • 🚀 使用 fetch 接管图片加载 - 避免浏览器并发请求上限导致的排队问题
  • AbortController 支持 - 组件卸载或 src 变化时自动取消未完成的请求
  • 🔄 智能降级 - 加载失败时自动回退到原始 src 或 fallback
  • 🛡️ CORS 错误检测 - 自动检测并上报 CORS 相关错误
  • 💾 内存管理 - 自动清理 Blob URL,防止内存泄漏
  • 📦 轻量级 - 无额外依赖,支持 Tree Shaking

安装

npm install fetch-controlled-image
# 或
yarn add fetch-controlled-image
# 或
pnpm add fetch-controlled-image

快速开始

import { FetchControlledImage } from "fetch-controlled-image";

function App() {
  return (
    <FetchControlledImage
      src="https://example.com/image.jpg"
      fallback="/placeholder.png"
      alt="示例图片"
      onLoad={() => console.log("图片加载成功")}
    />
  );
}

API

Props

| 属性 | 类型 | 必填 | 默认值 | 说明 | | ---------- | ------------------------- | ---- | ------- | --------------------------------------- | | src | string | 是 | - | 图片地址 | | fallback | string | 否 | - | 加载失败后显示的备用图片地址 | | onLoad | () => void | 否 | - | 加载成功后的回调函数 | | as | ComponentType \| string | 否 | 'img' | 自定义 img 元素,支持 styled-components | | alt | string | 否 | '' | 图片描述 | | logger | object | 否 | - | 外部传入的日志记录器 |

组件同时接受所有标准的 ImgHTMLAttributes<HTMLImageElement> 属性。

logger 接口

组件支持传入自定义的 logger 对象,用于错误上报:

const logger = {
  error: (
    eventName: string,
    error: Error,
    context: Record<string, unknown>
  ) => {
    // 上报错误到监控系统
    console.error(eventName, error, context);
  },
};

<FetchControlledImage src="https://example.com/image.jpg" logger={logger} />;

使用示例

基础用法

<FetchControlledImage
  src="https://example.com/image.jpg"
  fallback="/placeholder.png"
  alt="产品图片"
/>

使用 styled-components

import styled from "styled-components";

const StyledImage = styled.img`
  width: 100%;
  height: auto;
  border-radius: 8px;
`;

<FetchControlledImage
  as={StyledImage}
  src="https://example.com/image.jpg"
  fallback="/placeholder.png"
/>;

带加载回调

function ImageWithLoading({ src }) {
  const [loading, setLoading] = useState(true);

  return (
    <div>
      {loading && <Spinner />}
      <FetchControlledImage
        src={src}
        fallback="/placeholder.png"
        onLoad={() => setLoading(false)}
        style={{ display: loading ? "none" : "block" }}
      />
    </div>
  );
}

错误监控

const logger = {
  error: (eventName, error, context) => {
    // 发送到 Sentry
    Sentry.captureException(error, {
      tags: { event: eventName },
      extra: context,
    });
  },
};

<FetchControlledImage src="https://example.com/image.jpg" logger={logger} />;

工作原理

传统 <img> 标签的问题

浏览器对每个域名有并发请求限制(通常 6 个),当页面有大量图片时:

  1. 后面的请求会排队等待
  2. 用户看到的是空白或加载中状态
  3. 无法精确控制请求的取消

FetchControlledImage 的解决方案

┌─────────────────────────────────────────────────────────────┐
│                    FetchControlledImage                      │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. fetch(url) + AbortController                            │
│     ├── 可控的请求生命周期                                    │
│     └── 绕过浏览器图片请求队列                                │
│                                                              │
│  2. Blob + URL.createObjectURL                              │
│     └── 将响应转换为可显示的图片                              │
│                                                              │
│  3. 智能降级                                                 │
│     ├── fetch 失败 → 回退到原始 src                          │
│     └── 借助浏览器原生图片加载机制兜底                        │
│                                                              │
│  4. 内存管理                                                 │
│     ├── cleanup 时 revokeObjectURL                          │
│     └── src 变化时清理旧 URL                                 │
│                                                              │
└─────────────────────────────────────────────────────────────┘

开发

# 安装依赖
npm install

# 运行测试
npm test

# 构建
npm run build

# 测试覆盖率
npm run test:coverage

Playground

项目包含一个 Playground 用于演示和对比 FetchControlledImage 与原生 <img> 标签在大量并发图片加载场景下的表现差异。

环境要求

| 依赖 | 最低版本 | 说明 | | ------- | -------- | ------------------------------ | | Node.js | >= 16 | 运行前端开发服务器 | | pnpm | >= 8 | 包管理器 | | Rust | stable | 编译 image-server(基于 axum) |

安装 Rust 环境

Playground 的图片服务器使用 Rust 编写(基于 axum 框架),需要先安装 Rust 工具链:

macOS / Linux:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

安装完成后,重新加载环境变量:

source "$HOME/.cargo/env"

Windows:

https://rustup.rs 下载并运行 rustup-init.exe

验证安装:

rustc --version
cargo --version

更多信息请参考 Rust 官方安装文档:https://www.rust-lang.org/tools/install

启动流程

Playground 提供了一键启动脚本 playground/scripts/dev.mjs,会自动编排以下流程:

第一步:清理残留进程(避免端口冲突)
  │
  ▼
第二步:启动 image-server(cargo run,监听 http://127.0.0.1:3456)
  │  等待 2 秒
  ▼
第三步:启动 Vite 开发服务器(http://localhost:5173)
  │  等待 2 秒
  ▼
第四步:自动打开浏览器

一键启动(推荐):

cd playground
pnpm install
pnpm run dev

该命令会同时启动 Rust 图片服务器和 Vite 前端开发服务器,按 Ctrl+C 退出时会自动清理所有子进程。

手动分步启动:

如果需要分别控制两个服务,可以手动启动:

# 终端 1:启动图片服务器
cd playground/image-server
cargo run
# 服务启动后监听 http://127.0.0.1:3456

# 终端 2:启动前端
cd playground
pnpm install
pnpm run dev:vite

说明

  • 图片服务器从 playground/image-server/images/ 目录提供 100 张预下载的 300×200 JPEG 图片
  • 每个请求固定延迟 200ms,模拟真实网络环境
  • 响应禁用缓存(cache-control: no-store),确保每次测试都真正发起请求
  • 详见 playground/image-server/README.md

License

MIT