📅 我们继续 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
组件,一个具有现代风格的网站主页组件。🚀
感谢阅读,欢迎点赞、收藏和分享 😊