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>
<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"><divv-if="!loaded"class="h-10 w-10 animate-pulse rounded-full bg-gray-300"></div><imgv-else:src="data.profileImg"class="h-10 w-10 rounded-full object-cover" /><div class="ml-3"><divv-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>
<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
类实现脉冲动画效果:
<div class="h-[20px] w-3/4 animate-pulse rounded bg-gray-300"></div>
这是典型的骨架屏样式:灰色背景 + 动画闪烁,模拟文本或图像即将出现的效果。
💡 真实内容展示
当 loaded.value
变为 true
后,所有 v-else
分支被激活,显示真实的图片、标题、摘要和用户信息。
📦 数据模拟加载过程
我们使用 setTimeout
模拟了一个 2.5 秒的网络请求延迟:
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
添加组件预览常量:
{id: 24,title: 'Content Placeholder',image: 'https://50projects50days.com/img/projects-img/24-content-placeholder.png',link: 'ContentPlaceholder',},
router/index.js
中添加路由选项:
{path: '/ContentPlaceholder',name: 'ContentPlaceholder',component: () => import('@/projects/ContentPlaceholder.vue'),},
🏁 总结
适合用于新闻资讯、社交动态、产品列表等需要异步加载数据的场景。
你可以进一步扩展此组件的功能包括:
- ✅ 支持主题切换(深色/浅色模式)
- ✅ 封装为通用
<AppCardLoader />
组件,支持传入任意数据对象 - ✅ 支持多个卡片并列展示(卡片列表)
- ✅ 添加淡入动画(
fade-in
) - ✅ 支持响应式布局(适配移动端)
👉 下一篇,我们将完成StickyNavbar
组件,一个具有现代风格的网站主页组件。🚀
感谢阅读,欢迎点赞、收藏和分享 😊