📅 今天我们继续 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 样式重点讲解

🎯 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

每天造一个轮子,码力暴涨不是梦!🚀

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐