Appearance
菊花图和出错提示的正确实践 #
菊花图和出错提示往往是伴生的,菊花图出现之后很可能会看到出错提示,所以放在一起讲。
关于“失败提示”和“出错提示”的知识,请先阅读《API 响应规范》,现在假设你已经掌握了这些知识。简单说,失败提示就是用户操作失败后的提示,出错提示仅需考虑网络异常这一情况。
什么时候使用菊花图 #
GET 时:《首页首屏优化》一节说过:不确定高度、不确定内容数量,内容数量可能为 0 的内容区块,应使用菊花图,不要使用骨架屏和垫底素材。
POST、PUT、DELETE 时:视情况在按钮上添加菊花图,并且按钮置为
disabled
。
永远不要使用全屏的菊花图 #
永远不要用全屏的菊花图。因为:
用户对全屏菊花图的容忍度只有 5 秒钟,而网络超时的时长往往设定是 30 秒(为了让用户利用这个时间从弱网环境走到强网环境),让全屏菊花图旋转 30 秒会让用户发疯。
如果菊花图的存在时间少于半秒,那么会发生一次全屏闪烁,用户无法容忍全屏闪烁。
如果页面有多个区块,分别包含网络请求,那么全屏菊花图是代表哪个区块的加载动画呢?如果一个请求出错,另一个健康,那么菊花图消失还是保留?如果保留,那么出错 Toast 跟菊花图叠加?如果消失,那么用户能快速发现是哪个区块出错吗?
正确呈现菊花图和各种提示 #
GET | POST、PUT、DELETE | |
---|---|---|
菊花图 | 区块最上方居中放置菊花图,可加文字加载中 | 被点击的按钮加菊花图,且可能要给相关区块加遮罩防点击 |
为空、失败时提示 | 区块最上方居中写出无内容 或其他文字 | 屏幕中央 Toast 失败原因,可能 UI 也要有变化 |
网络出错时提示 | 区块最上方居中写出网络不给力 ,再放重新加载 按钮 | 屏幕中央 Toast 网络不给力 |
GET 下菊花图和各种提示解决方案 #
上文说过,菊花图只应当出现在 UI 区块的上方居中位置,而且与网络不给力
、重新加载
按钮有关联。那么,如何正确地、系统地使用菊花图、各种提示、重新加载
按钮呢?
准备一个标记叫做 loadingStatus #
loadingStatus
有 4 种状态(并不是简单的true
、false
):
空串:发起请求之前默认是空串,或者当有结果且结果有内容,也就是最常见的情况,此时应重置为空串。
pending
:当请求未决时,应设为pending
,然后提示菊花图即可。empty
:当请求有结果且是内容为空时,应设为empty
,然后提示没有内容
即可。networkError
:当请求有结果且是网络出错时,应设为networkError
,然后提示网络不给力
即可。
注意:
如果一个页面有多个区块要加载,可以给loadingStatus
加修饰词,比如文章和评论分别加载的时候:
js
export default {
data() {
return {
articleLoadingStatus: '',
article: {},
commentLoadingStatus: '',
commentList: [],
};
},
};
Vue HTML 模板范例 #
根据“内容区和异常的关系”以及“旧数据与新数据的关系”可分 3 种情况讨论,本文只讨论前两种:
内容与异常的关系 | 新数据与旧数据的关系 | 解释 | 解决方案 |
---|---|---|---|
当异常显现则内容区隐藏 | 新数据替换旧数据 | 最简单的情况,当数据极少且数量固定时使用 | 见下文 |
内容区始终显现,异常以遮罩盖住内容区 | 新数据替换旧数据 | PC 端常见于表格,移动端常见于内容极少的场合 | 见下文 |
内容区始终显现,异常显示在内容区尾部 | 新数据追加到旧数据尾部 | 即滚动加载,PC 端罕见,移动端常见 | 用组件库 |
- 假设当异常显现则内容区隐藏。以加载一个笔记列表为例,伪代码如下:
html
<div v-if="noteLoadingStatus === ''">
<div v-for="item in noteList" :key="item.id">
<!-- item 的内容 -->
</div>
</div>
<div v-else>
<u-loading-icon v-if="noteLoadingStatus === 'pending'"></u-loading-icon>
<div v-else-if="noteLoadingStatus === 'networkError'">
<div>网络不给力</div>
<u-button text="重新加载" class="margin-top-20" @click="getNoteList"></u-button>
</div>
<u-empty v-else mode="list" text="暂无内容"></u-empty>
</div>
- 假设异常以遮罩覆盖内容区,且新数据替换旧数据。伪代码如下:
html
<div class="relative">
<div v-for="item in noteList" :key="item.id">
<!-- item 的内容 -->
</div>
<div class="absolute w-100 top-0">
<u-loading-icon v-if="noteLoadingStatus === 'pending'"></u-loading-icon>
<div v-else-if="noteLoadingStatus === 'networkError'">
<div>网络不给力</div>
<u-button text="重新加载" class="margin-top-20" @click="getNoteList"></u-button>
</div>
<u-empty v-else mode="list" text="暂无内容"></u-empty>
</div>
</div>
解释一下:
将 ajax 的超时时间设为 30 秒,因为手机存在弱网可能性。(题外话:如果是 PC 平台就没必要 30 秒那么久,毕竟 PC 网络相对说非常稳定,我个人习惯是设为 11 秒。)
发送请求前,
noteLoadingStatus
设为pending
。假设 30 秒内收到响应,且数据有内容,那么
noteLoadingStatus
重置为空串,且v-for
生效,于是看到了正确内容。假设 30 秒内收到响应,但数据没内容,则
noteLoadingStatus
设为empty
,这时候模板显示下方分支,屏幕显示暂无内容
的分支。假设 30 秒内没有响应,则
noteLoadingStatus
设为networkError
,提示网络不给力
。相当重要的一点:在
网络不给力
的下方,必须放上重新加载
按钮,毕竟 Webview 是没有浏览器的“刷新”按钮的。
Vue JS 范例 #
伪代码如下,不解释:
js
getNoteList() {
this.noteLoadingStatus = 'pending';
return this.$api.getNoteList().then(
response => {
if (response.data && response.data.length) {
this.noteList = response.data;
this.noteLoadingStatus = '';
} else {
this.noteLoadingStatus = 'empty';
}
},
() => {
this.noteLoadingStatus = 'networkError';
}
);
},
#
POST PUT DELETE 下菊花图和各种提示解决方案 #
loadingStatus 只需要 true 和 false 状态 #
与 GET 下的loadingStatus
相比,POST PUT DELETE 因为只需要 Toast ,不需要在界面上保持状态,所以不需要那么多状态,仅true
和false
足够。
Vue HTML 模板范例 #
以删除表格一行,也就是删除一条记录为例,伪代码如下:
html
<van-button :disabled="delNoteLoading" @click="onClickDel">删除</van-button>
Vue JS 范例 #
伪代码如下,不解释:
js
delNote() {
this.delNoteLoading = true;
return this.$api.delNote({/* 参数 */}).then(
response => {
if (/* response.code 命中成功 code */) {
// ... 此处省略后续
} else {
// 如果没有失败可能性,则省略此分支
// 如果有 1 个分支,则直接处理,比如 Toast(response.msg);
// 如果有多个分支,则需要 switch...case... 处理
}
this.delNoteLoading = false;
},
() => {
Toast('网络不给力');
this.delNoteLoading = false;
}
);
},