前言
在前端开发中,我们经常用菊花图用以提升用户体验。出现菊花图的原因,99%的可能性是用户处于弱网或无网状态,1%的可能性是服务端数据处理太费时间,导致不能尽快响应。从网速上说,PC端从早年间就很少使用菊花图,毕竟入户宽带的网速还是很稳定的,所以菊花图更多用于移动端,本文就围绕uni-app移动端项目来讲。
菊花图与骨架屏
菊花图跟骨架屏是二选一的解决方案,本文只讨论菊花图,但是也说明一下二者的使用场景区别:
1、是否需要提前撑开高度?
需要撑开高度的场景,用骨架屏,比如一个页面的排除顶部导航、搜索条之外的主体内容,分为上中下三部分,那么此场景一定考虑使用骨架屏,因为上中下三部分的加载完成时间是不一定的,可能下部分最先加载完,上部分最后加载完,如果不使用骨架屏,那么上和中部分是0高度,你会先看到下部分的内容,然后“唰”的一下,上、中部分加载完成,又把下部分挤了下去,这就显得非常的业余,用户体验很差。
相反的,不需要提前撑开高度的场景,用菊花图足矣。比如主体内容只有一部分,那么没必要提前撑开高度,菊花图足矣。
2、加载失败的话,是否有垫底内容?
如果有垫底内容,当然用骨架屏,就比如APP首页第一屏通常是九宫格,如果加载失败,通常做法是使用垫底内容,也就是上一次打开APP时加载的九宫格会缓存到本地,这一次就作为垫底内容,同时,要弹出一个toast:网络不给力。
没有垫底内容,当然使用菊花图。
3、是否是用户关注的内容?
对于用户关注内容,可优先考虑菊花图,当然也不绝对。
对于用户不关注的内容,可优先使用骨架屏,当然也不绝对。例如UI有一个横条,里面有头像、昵称、用户等级,这是用户不关注的内容,因为每次都是同样的头像、昵称、用户等级,用骨架屏而且是无动画骨架屏更好。
菊花图的正确使用姿势
零、不要使用全屏菊花图
无论如何不要使用全屏菊花图,你在业界大型APP的使用中,你发现谁采用全屏菊花图了吗?一个都没有。
APP的一大原则就是尽量加载,也就是说页面模块能分着加载的,优先考虑分着加载,避免一个接口管所有内容,能读取缓存的,就拿缓存当垫底内容,这样的话,页面总会有部分区域是有内容的,那么你用全屏菊花图,意味着全页面被遮挡,也无法进行别的操作,是错误的做法。
一、首先准备一个flag叫做loadingStatus
loadingStatus有几种可能的值:
空串,发起ajax之前默认是空串,或者当有结果且结果有内容,也就是最正常的情况,也应重置为空串
'pending',当ajax发起且未收到请求时,应设为'pending',然后提示菊花图和“加载中”即可。
'empty',当ajax有结果且内容为空时,应设为'empty',然后提示“没有内容”即可。
'error',当ajax有结果且出错时,应设为'error',然后提示“网络不给力”即可。
这里多说一句:可能当前业界流行的报错信息是:“服务器连接超时”、“服务器500错误”、“一串英文提示”等等,事实上纯属多余,因为你无需跟用户做具体解释,原因是用户根本看不懂,什么叫超时?什么叫500错误?还给我英文提示?用户看得懂吗?并且,即便用户看得懂也没有什么鸟用,就比如你看到Windows的蓝屏提示,即便你看得懂,你又能如何呢?服务器500错误,用户又能找谁呢?很多程序员没有用户思维,只为了自己调试方便,于是把错误信息显示在UI中,愚蠢啊。所以:
网络连接超时:当然属于“网络不给力”,所以应该改成“网络不给力”。
500错误:首先500错误就是程序员造成的bug,道理上说不应该在线上服务器出现,即便真的是服务器BUG导致了出错,也不能承认,也要归咎于网络不给力,反正无法提供正常服务了,现在就是要保全服务器的面子,该嘴硬就要嘴硬,明白吗?所以也要改成“网络不给力”。
401错误:也就是用户权限过期,应当跳转到登录页。
-
后端程序员专门写的各种提示:
对于GET来讲,不要费心写提示!排除500错误的话,只剩下没有数据这一种可能性,前端还能不知道么?
对于其他方法来讲,只需要简单提示,例如:删除失败,无此记录,即可!当然,这不在本文讨论范围内。
二、html架构和JS代码
以Vue为例,伪代码如下:
<view v-if="loadingStatus === '' || (noteList.length && loadingStatus === 'pending')">
<view v-for="item in noteList" :key="item.id">
<!-- item的内容 -->
</view>
</view>
<view v-else>
<u-loading-icon v-if="loadingStatus === 'pending'" text="加载中"></u-loading-icon>
<view v-else-if="loadingStatus === 'error'">
<text>网络不给力</text>
<u-button text="重新加载" class="margin-top-20" @click="getNoteList"></u-button>
</view>
<u-empty v-else mode="list" text="暂无内容"></u-empty>
</view>
解释一下:
发起ajax前,loadingResult是空串,所以模板显示最上方的分支,而最上方的分支有可能是白板,有可能是旧内容,都无所谓。
将ajax的超时时间设为11秒,因为人的舒适等待极限就是11秒。
发送请求前,loadingResult设为'pending'
假设11秒内拿到了响应,且数据有内容,那么loadingResult重置为空串,且v-for生效,于是看到了正确内容。
假设11秒内拿到了响应,但数据没内容,则loadingResult设为'empty',这时候模板显示下方分支,屏幕显示“暂无内容”的分支。
假设11秒内没有响应,或有响应且是出错响应,则loadingResult设为'error',提示“网络不给力”
相当重要的一点:在报错文字的下方,必须放上“重新加载”按钮,毕竟APP是没有浏览器的“刷新”按钮的。
JS伪代码如下:
getNoteList() {
this.loadingStatus = 'pending';
return this.$api.getNoteList().then(
response => {
if (response.rows && response.rows.length) {
this.noteList = response.rows;
this.loadingStatus = '';
} else {
this.loadingStatus = 'empty';
}
},
() => {
this.loadingStatus = 'error';
}
);
},
三、配置ajax库响应拦截器
结合上文的知识,除了1. 有内容正常响应、2. 无内容正常响应、3. 请重新登录之外,一律算出错,而报错文本一律使用“网络不给力”就完事,尤其是没有必要区分无网络、服务器连接超时、服务器内部错误。
对于“请重新登录”,直接跳转到登录页面即可,原页面显示什么已经无所谓,所以不需要做考虑。