每日一题:骨架屏

介绍

在网络较慢,需要长时间等待加载的情况下,骨架屏可以在详细页面元素未展现时,把 DOM 结构通过简单的方块或圆形勾勒出来,相对于传统的转圈等待与白屏来说,用户体验更好。下面请根据题目要求,使用 Vue 封装一个灵活的骨架屏组件。

准备

开始答题前,需要先打开本题的项目代码文件夹,目录结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
├── components
│ ├── List
│ │ ├── content.js
│ │ └── index.js
│ └── Skeleton
│ ├── index.js
│ └── item.js
├── css
│ └── style.css
├── effect.gif
├── index.html
└── lib
└── vue.min.js

其中:

  • index.html 是主页面。
  • components/list 是提供的列表组件。
  • components/Skeleton 是骨架屏组件。
  • lib 是存放项目依赖的文件夹。
  • css 是存放项目样式的文件夹。
  • effect.gif 是项目目标完成效果图。

注意:打开环境后发现缺少项目代码,请复制下述命令至命令行进行下载。

1
2
3
4
wget https://labfile.oss.aliyuncs.com/courses/18213/dist_05.zip
unzip dist_05.zip
mv dist/* ./
rm -rf dist*

在浏览器中预览 index.html,显示如下所示:

初始效果

目标

找到 Skeleton/item.js 中的 TODO 部分,完成以下目标:

  1. 使用 index.html 中传递过去的数据 paragraph,并结合 Vue 递归组件的知识,完成骨架屏组件的编写。

paragraph 中的属性说明如下:

属性名 说明
type 骨架屏的容器类型,其属性值共有 row(行)、col(列)、rect(矩形)和 circle(圆形)四种类型。
style 容器类型 typerect(矩形)或 circle(圆形)对应的样式。
rowStyle 容器类型 typerow(行)时对应的样式。
colStyle 容器类型 typecol(列)时对应的样式。
rows 类型 typerow(行)的容器数组,里面的每一个对象都是嵌套的子模块,即,row(行)容器。
cols 类型 typecol(列)的容器数组,里面的每一个对象都是嵌套的子模块,即 col(列)容器。

本题中的骨架屏结构图及说明如下:

骨架屏结构示例图

  • 第一行(标记 1)为 1 个矩形占位。
  • 第二行(标记 2)分为两列(蓝色框),左边一列为三行组成,右边一列为 1 个矩形占位。
  • 第二行左边一列分别为如下构成:第一行(标记 ①)为 1 个矩形占位,第二行(标记 ②)为 2 个矩形占位,第三行(标记 ③)为 4 个圆形+1 个矩形占位。

typeDOM 结构的对应关系如下:

1
2
3
4
5
<div class="ske-${type}-container">
<div class="ske ske-${type}" :style="style">
<!-- ...... 根据类型判断此处是否需要添加元素。TIPS: row 里面可以继续嵌套 row -->
</div>
</div>

在上面的示例中:

  • ${type} 的值为对应的容器类型 rowcolrectcircle
  • style 表示 class="ske ske-${type}" 的元素对应的样式:

如果 ${type}rectcircle 则使用 style 属性。其 DOM 结构如下:

1
2
3
<div class="ske-rect-container">
<div class="ske ske-rect" :style="style"></div>
</div>

如果 ${type}rowcol,则使用 rowStyle 或者 colStyle 属性。其 DOM 结构如下:

1
2
3
4
<!-- rowStyle/colStyle 使用示例  -->
<div class="ske ske-row" :style="row.rowStyle" v-for="row in paragraph.rows">
<!--此处代码省略...-->
</div>
  1. 使用 index.html 中传递过去的数据 active 完成骨架屏组件的闪烁功能:如果 activetrue, 则给容器类型 typerect(矩形)或 circle(圆形)的组件内层 class="ske ske-rect"class="ske ske-circle" 的元素添加类名 .ske-ani;否则不添加。

完成后效果如下:

图片描述

规定

  • 请严格按照考试步骤操作,切勿修改考试默认提供项目中的文件名称、文件夹路径、class 名、id 名、图片名等,以免造成无法判题通过。
  • 满足需求后,保持 Web 服务处于可以正常访问状态,点击「提交检测」系统会自动检测。

判分标准

  • 本题完全实现题目目标得满分,否则得 0 分。

总通过次数: 61 | 总提交次数: 72 | 通过率: 84.7%

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/*
* 骨架屏渲染组件
*/
let ItemTemplate = `
<div :class='"ske-" + paragraph.type + "-container"'>
<div v-for="item in getParagraphArr(paragraph)" :class="getParagraphClass(item)" :style="getParagraphStyle(item)">
<item :paragraph="item" :acitve="active"></item>
</div>
</div>
`;
// TODO 请补充完整Template,完成组件代码编写
// console.log(paragraph);
Vue.component("item", {
name: "item",
template: ItemTemplate,
props: ["paragraph", "active"],
data() {},
mounted() {
// console.log(this.paragraph);
},
watch: {
paragraph(newValue, oldValue) {
// Log when paragraph prop changes
console.log("New paragraph value:", newValue);
}
},
methods: {
logParagraph() {
// Log the current value of paragraph
console.log("Current paragraph value:", this.paragraph);
},
getParagraphArr(paragraph) {
if (paragraph.type === 'row') {
return paragraph.rows
} else if (paragraph.type === 'col') {
return paragraph.cols
} else {
return []
}
},
getParagraphStyle(paragraph) {
const result = {};
if ('style' in paragraph) {
Object.assign(result, paragraph.style);
}
if ('rowStyle' in paragraph) {
Object.assign(result, paragraph.rowStyle);
}
if ('colStyle' in paragraph) {
Object.assign(result, paragraph.colStyle);
}
console.log(result);
return result;
},
getParagraphClass(paragraph) {
let base = ['ske', `ske-${paragraph.type}`]
if (this.active) {
base.push('ske-ani')
}
return base
}
},
});