50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | ContentPlaceholder(背景占位)

📅 我们继续 50 个小项目挑战!------ ContentPlaceholder组件

仓库地址:https://github.com/SunACong/50-vue-projects

项目预览地址:https://50-vue-projects.vercel.app/


使用 Vue 3 的 Composition API(<script setup>)结合 TailwindCSS 构建一个带有"骨架屏"加载动画的卡片组件。该组件会在数据加载期间显示占位符动画,在数据加载完成后展示真实内容。

🎯 组件目标

  • 创建一个独立、可复用的卡片组件
  • 使用 v-if 控制加载状态切换视图
  • 在数据加载时展示骨架屏动画(Skeleton Loader)
  • 数据加载完成后展示真实内容(标题、摘要、作者信息等)
  • 使用 TailwindCSS 快速构建 UI 样式
  • 模拟异步数据加载过程(使用 setTimeout

⚙️ 技术实现点

技术点 描述
Vue 3 <script setup> 使用响应式变量管理加载状态
ref 响应式变量 控制是否已加载完成
onMounted 生命周期钩子 模拟异步请求
setTimeout 模拟网络延迟
v-if 条件渲染 切换加载动画与真实内容
TailwindCSS 动画类 实现骨架屏的脉冲动画效果
TailwindCSS 布局类 构建卡片结构、图片裁剪、文字排版

🧱 组件实现

模板结构 <template>

html 复制代码
<template>
    <div class="flex h-screen items-center justify-center bg-gray-100">
        <div class="w-[350px] overflow-hidden rounded-xl bg-white shadow-lg">
            <!-- 图片头部 -->
            <div v-if="!loaded" class="h-[200px] w-full animate-pulse bg-gray-300" />
            <img v-else :src="data.headerImg" class="h-[200px] w-full object-cover" />

            <!-- 内容区域 -->
            <div class="space-y-4 p-6">
                <!-- 标题 -->
                <div v-if="!loaded" class="h-[20px] w-3/4 animate-pulse rounded bg-gray-300" />
                <h3 v-else class="text-xl font-bold text-gray-800">
                    {{ data.title }}
                </h3>

                <!-- 摘要 -->
                <div v-if="!loaded" class="space-y-2">
                    <div class="h-[10px] w-full animate-pulse rounded bg-gray-300"></div>
                    <div class="h-[10px] w-5/6 animate-pulse rounded bg-gray-300"></div>
                    <div class="h-[10px] w-2/3 animate-pulse rounded bg-gray-300"></div>
                </div>
                <p v-else class="text-gray-600">{{ data.excerpt }}</p>

                <!-- 作者 -->
                <div class="flex items-center">
                    <div
                        v-if="!loaded"
                        class="h-10 w-10 animate-pulse rounded-full bg-gray-300"></div>
                    <img
                        v-else
                        :src="data.profileImg"
                        class="h-10 w-10 rounded-full object-cover" />
                    <div class="ml-3">
                        <div
                            v-if="!loaded"
                            class="h-[10px] w-[100px] animate-pulse rounded bg-gray-300"></div>
                        <template v-else>
                            <strong class="text-gray-800">{{ data.name }}</strong>
                            <small class="text-gray-500">{{ data.date }}</small>
                        </template>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

脚本逻辑 <script setup>

js 复制代码
<script setup>
import { ref, onMounted } from 'vue'

const loaded = ref(false)

const data = {
    headerImg:
        'https://images.unsplash.com/photo-1496181133206-80ce9b88a853?auto=format&fit=crop&w=2102&q=80',
    title: 'Lorem ipsum dolor sit amet',
    excerpt: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolore perferendis.',
    profileImg: 'https://randomuser.me/api/portraits/men/45.jpg',
    name: 'John Doe',
    date: 'Oct 08, 2020',
}

onMounted(() => {
    setTimeout(() => {
        loaded.value = true
    }, 2500)
})
</script>

🔍 重点效果实现

✅ 加载动画(骨架屏)实现

我们通过 v-if="!loaded" 控制加载动画的显示,并使用 TailwindCSS 提供的 animate-pulse 类实现脉冲动画效果:

html 复制代码
<div class="h-[20px] w-3/4 animate-pulse rounded bg-gray-300"></div>

这是典型的骨架屏样式:灰色背景 + 动画闪烁,模拟文本或图像即将出现的效果。

💡 真实内容展示

loaded.value 变为 true 后,所有 v-else 分支被激活,显示真实的图片、标题、摘要和用户信息。

📦 数据模拟加载过程

我们使用 setTimeout 模拟了一个 2.5 秒的网络请求延迟:

js 复制代码
onMounted(() => {
    setTimeout(() => {
        loaded.value = true
    }, 2500)
})

这使得组件在挂载后不会立即展示内容,而是等待一定时间后才显示,模拟了真实场景中的异步加载行为。


🎨 TailwindCSS 样式重点讲解

类名 作用
h-screen, items-center, justify-center 全屏高度 + 居中布局
bg-gray-100 设置浅灰色背景
w-[350px], h-[200px] 固定宽度和高度
rounded-xl 圆角卡片
shadow-lg 添加阴影增强视觉层次
object-cover 图片自适应容器
p-6 内边距设置
space-y-4 子元素垂直间距
animate-pulse 骨架屏动画
bg-gray-300 占位块颜色
rounded-full 头像圆形裁剪

这些类组合起来实现了现代感十足的卡片样式和流畅的过渡动画。


📁 常量定义 + 组件路由

constants/index.js 添加组件预览常量:

js 复制代码
{
        id: 24,
        title: 'Content Placeholder',
        image: 'https://50projects50days.com/img/projects-img/24-content-placeholder.png',
        link: 'ContentPlaceholder',
    },

router/index.js 中添加路由选项:

js 复制代码
{
        path: '/ContentPlaceholder',
        name: 'ContentPlaceholder',
        component: () => import('@/projects/ContentPlaceholder.vue'),
    },

🏁 总结

适合用于新闻资讯、社交动态、产品列表等需要异步加载数据的场景。

你可以进一步扩展此组件的功能包括:

  • ✅ 支持主题切换(深色/浅色模式)
  • ✅ 封装为通用 <AppCardLoader /> 组件,支持传入任意数据对象
  • ✅ 支持多个卡片并列展示(卡片列表)
  • ✅ 添加淡入动画(fade-in
  • ✅ 支持响应式布局(适配移动端)

👉 下一篇,我们将完成StickyNavbar组件,一个具有现代风格的网站主页组件。🚀

感谢阅读,欢迎点赞、收藏和分享 😊

相关推荐
群联云防护小杜16 分钟前
构建分布式高防架构实现业务零中断
前端·网络·分布式·tcp/ip·安全·游戏·架构
ohMyGod_1231 小时前
React16,17,18,19新特性更新对比
前端·javascript·react.js
前端小趴菜051 小时前
React-forwardRef-useImperativeHandle
前端·vue.js·react.js
@大迁世界1 小时前
第1章 React组件开发基础
前端·javascript·react.js·前端框架·ecmascript
Hilaku1 小时前
从一个实战项目,看懂 `new DataTransfer()` 的三大妙用
前端·javascript·jquery
爱分享的程序员2 小时前
前端面试专栏-算法篇:20. 贪心算法与动态规划入门
前端·javascript·node.js
我想说一句2 小时前
事件委托与合成事件:前端性能优化的"偷懒"艺术
前端·javascript
爱泡脚的鸡腿2 小时前
Web第二次笔记
前端·javascript
良辰未晚2 小时前
Canvas 绘制模糊?那是你没搞懂 DPR!
前端·canvas
Dream耀2 小时前
React合成事件揭秘:高效事件处理的幕后机制
前端·javascript