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
Maintainers
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 个),当页面有大量图片时:
- 后面的请求会排队等待
- 用户看到的是空白或加载中状态
- 无法精确控制请求的取消
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:coveragePlayground
项目包含一个 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
