50天50个小项目 (React19 + Tailwindcss V4) ✨ | RandomImageGenerator(随机图像生成组件)
基于React19 和 Tailwind CSS 的随机图片流组件,可从Lorem Picsum服务获取不同尺寸的图片。主要功能包括:创建5行3列的网格布局,每张图片随机生成300-309像素的尺寸;使用React19 的组合式API管理图片数据;实现图片加载失败自动重试机制;通过map动态渲染图片网格。
📅 今天我们继续 50 个小项目挑战!——RandomImageGenerator组件
仓库地址:https://gitee.com/hhm-hhm/50days50projects.git
使用 React19 和 Tailwindcss V4 实用优先的样式框架,创建一个动态的随机图片流展示页面。这个组件会从 Lorem Picsum 服务获取不同尺寸的随机图片,并在图片加载失败时自动重试,非常适合用作作品集、博客或设计类网站的背景或内容填充。
🌀 组件目标
- 创建一个包含多张随机图片的网格布局
- 每张图片从 Lorem Picsum 服务获取,尺寸随机
- 图片加载失败时自动尝试加载新的随机图片
- 实现页面加载时自动初始化图片流
- 利用 Tailwind CSS 快速构建基础样式,并通过内联样式实现精确控制
🔧 RandomImageGenerator.tsx组件实现
import { useState, useEffect } from 'react'
interface ImageItem {
url: string
width: number
height: number
}
const RandomImageGenerator = () => {
const [images, setImages] = useState<ImageItem[]>([])
const baseURL = 'https://picsum.photos'
const rows = 5
const columns = 3
const getRandomSize = (): number => Math.floor(Math.random() * 10) + 300
const generateImages = () => {
const newImages: ImageItem[] = []
for (let i = 0; i < rows * columns; i++) {
const width = getRandomSize()
const height = getRandomSize()
// 使用随机 query 防止浏览器缓存重复图片
const url = `${baseURL}/${width}/${height}?random=${Math.random()}`
newImages.push({ url, width, height })
}
setImages(newImages)
}
const handleImageError = (index: number) => {
const width = getRandomSize()
const height = getRandomSize()
const newUrl = `${baseURL}/${width}/${height}?random=${Math.random()}`
setImages((prev) =>
prev.map((img, i) => (i === index ? { ...img, url: newUrl, width, height } : img))
)
}
// 组件挂载时生成图片
useEffect(() => {
generateImages()
}, [])
return (
<div className="font-roboto flex min-h-screen flex-col items-center justify-center bg-gray-700 p-4">
<h1 className="title my-6 text-center text-3xl font-bold">Random Image Feed</h1>
<div className="container mx-auto flex max-w-6xl flex-wrap items-center justify-center">
{images.map((image, index) => (
<div key={index} className="m-3">
<img
src={image.url}
alt={`Random image ${index + 1}`}
className="h-full w-full max-w-full object-cover"
style={{ width: `${image.width}px`, height: `${image.height}px` }}
onError={() => handleImageError(index)}
/>
</div>
))}
</div>
<div className="fixed right-8 bottom-5 z-100 text-2xl text-red-500">
CSDN@Hao_Harrision
</div>
</div>
)
}
export default RandomImageGenerator
🔄 转换说明(Vue → React)
| 功能 | Vue 3 实现 | React + TS 实现 | 说明 |
|---|---|---|---|
| 状态管理 | ref([]) → images |
useState<ImageItem[]>([]) |
使用泛型确保类型安全 |
| 生命周期 | onMounted() |
useEffect(() => { ... }, []) |
等效于组件挂载后执行一次 |
| 列表渲染 | v-for="(image, index) in images" |
{images.map((image, index) => ...)} |
React 使用 .map() 渲染列表 |
| 动态样式 | :style="{ width: ..., height: ... }" |
style={{ width: ..., height: ... }} |
内联样式对象写法 |
| 事件处理 | @error="handleImageError(index)" |
onError={() => handleImageError(index)} |
React 事件需传入函数,不能直接调用 |
| 更新特定项 | 直接修改 images.value[index] |
使用 setImages(prev => prev.map(...)) |
React 状态不可变,必须返回新数组 |
| 字体类 | font-roboto |
保留(需确保已加载 Roboto 字体) | 若未引入,可替换为 font-sans |
📝 关键细节说明
1. 不可变状态更新(Immutability)
在 Vue 中可以直接修改 images.value[index],但在 React 中必须创建新数组:
setImages(prev =>
prev.map((img, i) => i === index ? { ...img, url, width, height } : img)
);
✅ 这是 React 状态更新的核心原则。
2. 错误处理中的闭包问题
使用 onError={() => handleImageError(index)} 而不是 onError={handleImageError(index)},
否则会在渲染时立即执行函数,而非在图片加载失败时触发。
3. 随机参数防缓存
?random=${Math.random()}
✅ 与 Vue 版本一致,确保每次请求都是新 URL,避免浏览器返回缓存的相同图片。
4. 字体注意事项
若项目未加载 Roboto 字体,请在 index.html 中添加:
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
或替换 font-roboto 为 font-sans 以使用系统默认字体。
5. 性能提示(可选优化)
- 如果图片数量很大,可考虑使用
React.memo包裹<img>(但此处非必需) - 图片尺寸是随机的,因此每张图宽高不同,形成“瀑布流”效果
✅ 总结
该 RandomImageGenerator.tsx 组件:
- 完全复刻了 Vue 版本的 随机尺寸图片网格 + 错误重试 功能
- 使用 TypeScript 接口 明确定义图片数据结构
- 遵循 React 不可变状态原则
- 兼容 Tailwind CSS 样式体系
- 可直接用于 Vite + React + TS 项目
🎨 TailwindCSS 样式重点讲解
| 类名 | 作用 |
|---|---|
font-roboto |
使用 Roboto 字体 |
min-h-screen |
最小高度为视口高度 |
flex / flex-col / items-center / justify-center |
Flexbox 布局,垂直居中 |
bg-white |
白色背景 |
p-4 |
内边距 |
text-3xl / text-center / font-bold / my-6 |
标题样式 |
max-w-6xl |
最大宽度 (80rem ≈ 1280px) |
flex-wrap |
允许子元素换行 |
mx-auto |
水平居中 |
m-3 |
外边距 (0.75rem),形成网格间隙 |
w-full / h-full / max-w-full / object-cover |
图片样式,确保按比例覆盖容器 |
:style |
动态设置精确的像素尺寸 |
🦌 路由组件 + 常量定义
router/index.tsx 中 children数组中添加子路由
{
path: '/',
element: <App />,
children: [
...
{
path: '/RandomImageGenerator',
lazy: () =>
import('@/projects/RandomImageGenerator').then((mod) => ({
Component: mod.default,
})),
},
],
},
constants/index.tsx 添加组件预览常量
import demo48Img from '@/assets/pic-demo/demo-48.png'
省略部分....
export const projectList: ProjectItem[] = [
省略部分....
{
id: 48,
title: 'Random Image Generator',
image: demo48Img,
link: 'RandomImageGenerator',
},
]
🚀 小结
项目很好地展示了如何利用外部 API(Lorem Picsum)快速搭建视觉内容,并通过 @error 事件和响应式数据更新来提升用户体验的健壮性。通过结合 React19 和 Tailwind CSS 的实用类,我们实现了图片的动态生成、精确尺寸控制和错误恢复机制。 happy coding! ✨
你可以扩展以下功能:
✅ 添加加载状态:在图片加载完成前显示骨架屏 (Skeleton Screen) 或加载动画。
✅ 实现懒加载:提升初始加载性能。
✅ 增加交互:点击图片放大预览,或添加喜欢/收藏功能。
✅ 搜索或筛选:允许用户通过关键词搜索 Picsum 图片(Picsum 支持 ?grayscale 等参数)。
✅ 动态调整网格:提供按钮让用户选择每行显示的图片数量。
✅ 错误边界:如果 handleImageError 也失败(如网络完全断开),可以显示一个通用的“加载失败”占位符。
📅 明日预告: 我们将完成TodoList组件,一个视觉舒适的TODO UI设计。🚀
感谢阅读,欢迎点赞、收藏和分享 😊
原文链接:https://blog.csdn.net/qq_44808710/article/details/149796496
每天造一个轮子,码力暴涨不是梦!🚀
更多推荐



所有评论(0)