# uni-app 页面加载提示对比分析
## 概述
本文档对比分析了 uni-app 项目中两种常见的页面加载提示实现方式:全局加载提示和骨架屏加载动画。通过对比 `my-patients.vue` 和 `message-detail.vue` 两个页面的具体实现,总结各自的特点、适用场景和优缺点。
## 实现方式对比
### 1. 全局加载提示(my-patients.vue)
#### 实现原理
- 使用 uni-app 提供的 `uni.showLoading()` 和 `uni.hideLoading()` API
- 在数据请求开始时显示全局模态框,请求完成后隐藏
#### 代码示例
```typescript
const fetchPatients = async () => {
uni.showLoading({ title: '加载中...' })
try {
// 数据请求逻辑
const response = await listUserBindingsByBoundUser(...)
// 处理响应
} catch (error) {
// 错误处理
} finally {
uni.hideLoading()
}
}
```
#### 特点
- **优点**:
- 实现简单,只需几行代码
- 用户体验一致,uni-app 原生支持
- 阻塞用户交互,防止重复操作
- **缺点**:
- 全局模态框会遮挡整个页面
- 无法显示内容预览或进度信息
- 在小程序中可能有样式限制
#### 适用场景
- 数据加载时间较短(< 3秒)
- 页面内容不需要预览
- 希望阻止用户在加载期间进行其他操作
### 2. 骨架屏加载动画(message-detail.vue)
#### 实现原理
- 使用 Vue 的条件渲染 `v-if="loading"`
- 在模板中创建模拟真实内容的占位符结构
- 通过 CSS 动画实现闪烁效果
#### 代码示例
```vue
```
#### CSS 样式示例
```css
.skeleton-line {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
}
@keyframes skeleton-loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
```
#### 特点
- **优点**:
- 视觉体验更好,能预览页面布局
- 不阻塞用户交互,可以浏览其他内容
- 自定义程度高,可设计独特的加载动画
- 提升用户感知的加载速度
- **缺点**:
- 实现复杂度较高,需要设计骨架结构
- 需要维护两套布局(骨架和真实内容)
- CSS 动画可能影响性能
#### 适用场景
- 数据加载时间较长(> 2秒)
- 页面布局复杂,需要内容预览
- 希望保持用户交互流畅性
- 追求更好的视觉体验
## 对比总结
| 特性 | 全局加载提示 | 骨架屏加载动画 |
|------|-------------|---------------|
| 实现复杂度 | 低 | 中等 |
| 用户体验 | 一般 | 优秀 |
| 视觉效果 | 简单 | 丰富 |
| 交互阻塞 | 是 | 否 |
| 自定义程度 | 低 | 高 |
| 性能影响 | 低 | 中等 |
| 适用数据量 | 小数据 | 大数据/复杂页面 |
## 选择建议
1. **简单页面/快速加载**:推荐使用全局加载提示
2. **复杂页面/慢速加载**:推荐使用骨架屏
3. **混合使用**:可以结合两者,先显示骨架屏,再叠加全局提示
## 最佳实践
1. 根据页面复杂度选择合适的加载方式
2. 设置合理的超时时间,避免无限加载
3. 在错误情况下也要隐藏加载状态
4. 考虑不同平台的兼容性
5. 测试加载状态下的用户交互
## 常见问题与解决方案
### 骨架屏与实际内容同时显示问题
#### 问题现象
在使用骨架屏加载动画时,从其他页面(如编辑页面)返回时,骨架屏下方残留有之前的实际列表内容,导致加载状态与旧数据同时显示,影响用户体验。
#### 问题原因
模板中骨架屏使用 `v-if="loading"` 条件显示,但实际内容列表没有相应的条件隐藏。在数据重新加载期间,`loading` 为 true 时骨架屏显示,但旧的列表数据仍然可见,因为没有 `v-if="!loading"` 限制。
#### 解决办法
将实际内容区域包装在 `v-if="!loading"` 条件中,确保加载时只显示骨架屏,加载完成后显示实际内容。
**错误示例:**
```vue
```
**正确示例:**
```vue
```
#### 经验教训
- 骨架屏和实际内容应使用互斥的条件渲染,避免同时显示
- 在页面重新加载(如 `onShow` 触发)时,确保旧数据被正确隐藏
- 参考 `message-detail.vue` 的实现模式,确保加载状态管理的完整性
- 骨架屏设计时应区分动态加载内容和静态资源,避免对静态元素添加不必要的加载动画
### 骨架屏动画效果优化(静态vs动态)
#### 问题现象
在对比 `news.vue` 和 `message-detail.vue` 的骨架屏实现时,发现 `news.vue` 的骨架屏看起来不够真实、缺乏动态感,而 `message-detail.vue` 的骨架屏更具真实感和流畅性。
#### 问题原因
`news.vue` 的骨架屏使用静态的渐变背景(`linear-gradient(90deg, #eee, #f6f6f6)`),没有动画效果,只是固定的颜色块;而 `message-detail.vue` 使用了动态的流动动画,通过 CSS `animation` 和 `@keyframes` 让背景从左到右移动,模拟内容正在加载的过程。
#### 解决办法
为骨架屏元素添加 CSS 动画效果,使其产生流动感,提升真实感。
**具体修改步骤:**
1. 修改骨架元素的背景样式:
```css
.skeleton-cover,
.skeleton-title,
.skeleton-author,
.skeleton-summary {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
}
```
2. 添加动画定义:
```css
@keyframes skeleton-loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
```
#### 优化效果
- 静态骨架屏 → 动态流动动画
- 提升用户感知的加载真实感
- 视觉体验更加流畅和专业
#### 经验教训
- 骨架屏不仅仅是占位符,更应该模拟真实的加载过程
- 适当的动画效果能显著提升用户体验
- 参考现有优秀实现(如 `message-detail.vue`),统一项目内的骨架屏风格
### 骨架屏布局真实性优化
#### 问题现象
在对比 `news.vue` 和 `message-detail.vue` 的骨架屏实现时,发现 `news.vue` 的骨架屏布局与实际内容结构不匹配,看起来不够真实,而 `message-detail.vue` 的骨架屏更贴近实际内容的布局。
#### 问题原因
`news.vue` 的骨架屏结构是封面占位符 + 标题 + 作者 + 摘要的水平布局,但实际新闻卡片的布局是标题在顶部,然后是图片 + 摘要 + 作者的垂直布局。骨架屏没有准确模拟真实内容的视觉层次和排列方式。
#### 解决办法
调整骨架屏的 HTML 结构和 CSS 样式,使其完全匹配实际内容的布局。
**具体修改步骤:**
1. 修改骨架屏的 HTML 结构:
```vue
```
2. 调整对应的 CSS 样式:
```css
.skeleton-card {
background: #fff;
border-radius: 12rpx;
margin-bottom: 18rpx;
overflow: hidden;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.15);
}
.skeleton-title {
width: 60%;
height: 34rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
margin: 20rpx;
border-radius: 6rpx;
}
.skeleton-content {
display: flex;
padding: 20rpx;
gap: 20rpx;
align-items: center;
}
.skeleton-cover {
width: 220rpx;
height: 150rpx;
border-radius: 10rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-meta {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.skeleton-summary {
width: 100%;
height: 28rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
margin-bottom: 12rpx;
border-radius: 6rpx;
}
.skeleton-author {
width: 50%;
height: 24rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
border-radius: 6rpx;
}
```
#### 优化效果
- 骨架屏布局完全匹配实际内容结构
- 提升用户对加载状态的真实感
- 改善整体视觉体验
#### 经验教训
- 骨架屏的设计应该精确模拟真实内容的布局结构
- 不仅仅是占位符,更要考虑视觉层次和排列方式
- 定期对比骨架屏与实际内容的差异,确保一致性
## 相关文件
- `src/pages/doctor/index/my-patients.vue` - 全局加载提示示例
- `src/pages/public/message-detail.vue` - 骨架屏加载动画示例
- `src/pages/doctor/manage/news.vue` - 骨架屏逻辑修复示例
- `src/pages/doctor/index/index.vue` - 骨架屏逻辑修复示例(用户信息和患者动态)
d:\慢病APP\Dev\uniapp-ts\docs\uni-app 页面加载提示对比分析.md