先看目录,大家按需取用,当然更建议全文阅读(全文7756字,建议阅读20分钟)

undefined

undefined

大部分设计师应该都遇到过这种场景:在做设计前并没有考虑到加载,但在进行还原度走查或者验收的时候才发现,由于某些页面数据请求较慢,导致在页面中会出现空屏状态。这时才想起需要在这些页面添加动画来承载页面的过渡。

归根结底是因为设计师在设计过程中,更多关注页面本身,而很少去思考页面在呈现过程中何时会出现白屏状态,都是后知后觉去补充完善。加载作为必备的一环,却总是被忽略。目前很多B端产品在这方面都没有一个系统合理的规划,导致系统的加载体验缺失或者不统一。

因此希望这篇文章能够讲清楚中后台加载出现的场景和规则,对需要深入了解加载以及在制定加载规则的设计师朋友们带来一些帮助。

undefined

加载通俗来讲就是用户从触发页面操作,到页面最终呈现的一个等待过程。这个过程始终存在不可避免,只是时间有快有慢。快的加载只需要几百毫秒就结束,但慢的加载就可能会达到几秒甚至十几秒,让人产生焦虑。

而为什么会有如此大的差距,这就需要从页面渲染的整体过程来进行说明。当我们从浏览器输入网址,再到我们看到完整页面的这个过程,网页到底经过了哪些步骤呢。经过资料查询和与前端确认,整体过程可以阐述如下:

从这里我们可以看到页面的呈现是程序经过了一系列操作才最终呈现到我们面前的。在这里可以将其简化为四个阶段:用户操作功能→页面向服务器发送请求→服务器接受并返回数据→页面呈现。

而决定整个页面加载快慢的就在于请求与数据这里。一般我们可以将页面的数据分为2种类型:静态资源和动态资源。

静态资源:前端的固定页面,这里面包含HTML、CSS、JS、图片等等,不需要查数据库也不需要程序处理,直接就能够显示的页面。可以简单理解为你页面上的固定字段和结构。这种页面一般加载速度比较快,比如我们看到的展示型官网一般都是前端写好的静态资源。

动态资源:而对于页面上的动态变化的,需要程序处理或者从数据库中读数据,能根据不同的条件在页面显示不同的数据,比如表格数据、统计数据等称为动态资源,这种都需要调用后台接口来进行返回,因此加载速度相比于静态资源就会更慢。

而它们的区分点用下图可以表示:

可以看到静态资源基本是直接返回,而动态资源还需要连接数据库调取资源,尤其是在遇到数据库调取较慢时就会花费更多时间。而我们加载缓慢的大多数问题,也基本上更多出现在动态资源的获取上。

undefined

当我们清楚加载形成的原因,接下来要做的就是在需要加载的地方对其进行处理。这也是在设计过程中我们经常遗漏的地方。我们先看一下目前常见的2种处理方式:「默认处理」和「使用进度指示器」

2.1程序默认处理方式:直接显示

当我们对加载过程不进行任何处理时,程序就会以默认的方式进行-即根据资源获取速度一步步呈现。可以看到嘿店后台的处理过程就是一种默认处理方式:

但是这种做法就会导致我们在页面加载过程中会出现空屏状态,比如上图切换到概览时出现了空屏状态,尤其是当遇到了网络缓慢的情况,会造成在加载时页面停留在当前状态下不动,往往会让用户陷入到「系统故障」的错觉。

2.2通用处理方式:进度指示器

这个名词说起来可能比较陌生,它指代的就是我们平时经常看到的加载动画、骨架屏或者进度条等。进度指示器的作用就是告知用户当前页面并没有故障,而是正在读取数据。

通过添加进度指示器来对空屏状态进行承载,能够减轻用户的焦虑感。目前很多B端产品更通用的形式是添加动画来过渡。

但是在体验过程中很容易发现一个问题,就是在产品的整体加载过程中,某些空屏状态是没被加载动画覆盖到的。当这些没被覆盖到的加载过程过长时,很容易出现焦虑感。比如神策数据在左侧列表切换时的加载过程就没被覆盖:

想要更全面的把加载动画覆盖到所有页面,我们就需要弄清页面在哪些状态下会出现空屏状态,从而制定统一的加载动画规则。这个问题可以先思考一下,后面解答。

undefined

在制定统一加载规则之前,我们需要明确一个概念,就是加载的模态与非模态。

3.1模态加载

模态加载的含义就是当前加载会中断用户其余操作,用户在这个期间只能等待加载而不能进行其他操作(有的模态会提供取消按钮)。如果你的页面涉及到以下2种情况,可以考虑使用:

1.用户当前的操作本身不能与其他操作同时进行。比如系统更新,或者工具类的导入导出相关操作,我们只能等待更新、导出完成才能继续进行后续的操作。这时候可以使用模态加载来承载,比如protopie的导入操作;

2.当用户进入到一个全新的页面时,这个时候页面什么都没有,我们只能等待页面加载完成才能进行后续的操作,因此在这种情况下也可以采用模态加载,比如我们刚进入Asanan产品页面看到的首屏加载动画:

除了上述2种情况外,你也可以根据你的业务场景来进行模态加载的选择,但建议是尽量少用模态加载,其会对用户的打断和干扰性比较强。

3.2非模态加载

非模态加载的话,这种加载通常只会出现在需要加载的部分,不会中断用户其他操作。对用户干扰比较小。比如我们常见的功能切换加载、数据筛选加载等都属于非模态加载。

非模态加载相比于模态加载会更轻量,因为用户随时都可以打断也不会影响到其他操作。因此建议在大部分情况下都可以使用非模态弹窗来进行承载,比如飞书的左侧栏切换操作:

undefined

接下来我们进入正题,在这里我从加载的角度将网页整体加载过程分为呈现加载规则和操作加载规则。

我们先谈页面呈现规则。前面已经明确加载产生的原因和类型后,我们就可以开始为我们的产品制定统一的加载规则,以保证后续页面加载的一致性。

4.1 页面的加载过程拆解

在拆解页面的加载过程前,我们进一步阐述之前提到的渲染原则,从前文中提到最后会经过「解码」和「渲染」2个步骤,这也是决定了我们看到的页面的最终呈现顺序:

1.页面的渲染顺序

我们看到的页面是由HTML、CSS和JavaScript来进行构成的。HTML可以看作最简单的框架、CSS则是赋予了框架样式,JavaScript则是负责页面中的交互(当然JS也可以控制样式,这里只描述主要功能)。

页面在「解码」阶段,拿到的HTML文档会被解码为DOM树,同时将CSS文件解析成CSSOM,这两者结合后一起渲染页面,JS一般是在最后渲染。所以逻辑上就是框架和样式一起渲染,最后渲染交互。视觉的角度来讲就是先看到元素样式,然后才能进行对应操作。

2.页面呈现的视觉顺序

由于浏览器渲染顺序一般会根据代码的顺序进行渲染,而代码在页面中的构建一般也是按照页面的上下和左右的顺序去写的,因此我们看到的页面的视觉呈现顺序也是遵循从上到下,从左到右。

所以几乎所有的产品都是先看到顶栏,然后左侧边栏,然后其他内容。因此我们可以通过这个原则来拆解对应的页面,我们按照页面常规结构可以将其分为三个加载部分:顶栏、左侧导航栏、内容区域。而其加载顺序如图所示:

当知道对应页面的渲染顺序后,我们就能够清楚在页面渲染的过程中哪些地方会出现空屏了。因此将页面加载过程拆解为如下顺序:

undefined

我们的加载动画需要承载的就是上述这些白屏的地方,从而将加载细化为三种场景的拆分:全局加载+局部加载+内部加载。如图所示:

undefined

通过这三种加载就可以涵盖页面所涉及到的所有部分。接下来详细阐述一下这三种类型的定义和用法。

4.2 全局加载

如上图所示,全局加载的目的是为了覆盖用户从输入网址到页面的资源渲染的这个中间过程的空屏状态而存在的。而这种状态会涉及三种场景:

1.通过网址进入到一个新的页面时;

2.通过鼠标右键或网页刷新按钮对该页面刷新时;

3.通过页面操作需要新开页面时。

该全局加载的动画构成为:

1:覆盖整个页面的加载,由纯白色+加载动画构成;

2:加载类型为模态加载,因为用户在这种页面状态下并不能进行其它操作。

undefined

但在这里我们需要注意全局加载的开始和结束时间:

开始时间:当进入页面时立即呈现加载动画;

结束时间:当页面加载出顶栏的时候,此时停止加载。

为何要这么处理结束时间,原因其实图中已经给出了。这里再解释一下原则:只要有页面资源返回,我们的全局加载动画就会结束,随后启用局部加载来承接后续的状态,避免用户在当前状态长时间等待。而顶栏在一般情况下都是最先加载出来的,所以以顶栏出现作为结束全局加载的标准。当然如果你的结构没有顶栏,可以以左侧栏来作为结束标准。

4.3 局部加载

局部加载是用在页面整体框架加载的过程中,承接在全局加载后。局部加载的使用场景可以概括如下:

1.顶栏加载结束后,用在剩余框架的加载效果(具体体现为左侧边栏和右侧内容区域);

2.对于涉及到局部页面的切换操作都会进行局部加载(比如左侧边栏的切换);

光看文字可能不是特别清晰,在这里可以举一个动态的例子来帮助理解:


上述展示的是在页面呈现的时候一个完整的局部加载。在这里需要注意的是我们首次渲染和第二次渲染在加载动画上是可以有区分的,可以通过程序控制让这种加载动画只在初次加载时出现。

第一次加载时出现是因为我们需要有加载动画来承接框架首次加载的空屏时间:

但第二次的时候由于有缓存(缓存会加载我们的静态资源,能够让我们的页面框架在第二次时更快显示),这样在读取框架时基本不需要加载,我们就可以通过程序来直接去掉其中的局部加载动画,直接显示框架来进行呈现。

undefined

目前在飞书和钉钉等B端产品后台均采用了这种处理方式。可以看到图中我在第一次切换到角色管理时是有碰撞小球的局部动画存在的,而第二次再次进入时则直接出现框架。这样既能够保证加载动画能够覆盖所有的空屏状态,又避免了局部加载动画的频繁出现。

4.4 内部加载

内部加载是用在页面内部不同模块数据间的加载。当框架都已经加载结束,但某些数据由于接口比较慢,此时还没有返回,这时我们就可以用内部加载来进行承载。这应该是我们更常见的加载情况:

在进行内部加载的时候,需要注意,内部加载的时候一般标题是存在的,因为标题基本都是固定的,所以能够很快被拉取。比如Zoho的内部加载,加载时标题已经出现了:

通过这三种类型的加载,能够覆盖从用户输入网址,到页面渲染完成这个页面呈现过程中的全部加载场景。

undefined

说完页面的呈现规则,再谈页面操作加载规则。页面的操作其实对应的是页面控件的反馈。而我们的常用的控件比如有按钮、tab切换等。我们不仅需要考虑控件本身的加载状态,而且需要考虑到控件影响的其他页面范围。

5.1需要考虑控件本身加载

控件的加载并不是随时都需要,我们要根据实际的数据量大小及业务场景来选择性使用。目前我们需要考虑的控件及其加载状态主要有如下:

undefined

比如图中的按钮的加载状态是必备的,在很多场景下都会用到。但是下拉列表和级联列表,在一般的场景下都不太需要进行加载,当遇到列表中的数据特别多或者调取特别慢时就可以考虑为其加上加载状态。

5.2当控件操作会影响到页面其他元素

这种控件比如日期筛选、表格筛选或者保存等操作,当你切换筛选条件后所有与其相关的页面元素都会发生变化。在这种情况下我们需要考虑到被影响元素的状态。目前的设计实现上有两种做法:

1.被影响元素不可被操作,且该区域有遮罩+加载动画来覆盖;

2.被影响元素可进行操作,无任何动画,但操作并不会影响之前提交的结果;


这两种方案一种是利用遮罩防止用户无效操作,另一种则是保持了足够的操作自由性。个人在这里更倾向于处理方式1,我认为被影响的元素是需要有遮罩+动画的,来避免用户在加载期间对其进行无效操作,比如示例中方案2后面修改的名称是没有被系统保存的。

需要注意的是如果在产品中使用方案1,我们的遮罩区域只需要覆盖被影响的元素就可以了(保存这种可以将按钮一起覆盖),比如通过筛选导致图表数据发生变化,这时只需覆盖图表区域,而不用覆盖筛选区域。这样的好处是当某些筛选数据出现加载过慢问题时,用户可以通过切换筛选项来打断当前加载。

上述描述的操作都是针对于当前页加载。当控件导致页面刷新或者跳转时,则整体遵照页面呈现的规则进行加载。

undefined

上面阐述的加载已经完全能够覆盖我们页面整体的加载,但是还是需要提及一下骨架屏和进度条这两种加载形式。

undefined

先说骨架屏。实际上骨架屏的使用效果体验是优于加载动画的体验的(骨架屏的加入使用会让用户感觉网页出现的更快)。那么为什么在大部分的B端业务页面中没被用到呢,主要有2点原因:

1.每种骨架屏都需要进行对应的定制开发,需要与加载后的页面框架保持一致,这会增加了开发成本。

2.中后台的业务界面对来说固定结构的页面会较少,这对于骨架屏的使用机会就相对较少。

个人认为在前期可以以统一加载体验为主,在后期业务相对成熟后,可以针对固定结构,通过骨架屏的使用优化加载体验。

再说进度条。我理解的进度条的使用条件:对于加载时间较长的规定场景可以考虑用进度条来承载,比如我们常见的游戏加载中进度条就用得比较多,因为游戏一般资源比较大。还比如figma在进入设计文件的过程中也采用了进度条加载也是因为设计文件可能会很大。

进度条在特定场景下能够缓解用户焦虑,让用户知道进展。但进度条在多数情况下都抓取不到程序的真实进度,这也会导致有时候我们看着加载到99%,那最后的1%迟迟加载不出来的的原因。目前很多中后台产品对于进度条加载使用相对较少的原因,很大程度是没有那种加载特别长的场景。

因此这两种加载场景的使用会更加定制化,如果需要使用请根据具体的业务场景来进行选择。

如果把加载动画等比作页面加载的外在,那么缓存和加载策略则是页面加载的内核,合理使用缓存和加载策略可以从根本上提升我们页面的加载速度,让用户更快速地看到页面。

7.1 关于页面的资源缓存

大家肯定听过缓存这个概念,前后端都可以使用缓存。缓存就是数据交换的缓冲区(称作Cache),是存贮数据(使用频繁的数据)的临时地方。在这里主要讲一下浏览器缓存:

undefined

从这张图可以看出,服务器在请求数据时,会进行缓存的判断,如果有缓存则首先读取缓存,如果没有则从服务器中拿取。调取缓存会在很大程度上提升页面的加载速度,缩短了从服务获取数据的时间。通常浏览器会主动对页面的静态资源进行对应的缓存,这也就解释了我们第二次进入页面会比初次进入页面时加载快的原因。

但程序其实也可以对动态资源(也就是需要从数据库等拿到的资源)进行缓存的,并且可以设置缓存的过期时间(比如设置过期时间为1小时,那么1小时过后就会重新拉取新资源)。对于某些动态资源拉取过慢并且更新频率不高的我们可以采用动态资源缓存的策略,从而提升整体的页面加载体验。

7.2 加载策略的正确使用

现阶段我们常用的主要有下列6种加载策略:

加载策略的本质就是通过对应的加载设置来缩短产品与服务器交换数据的时间,接下来我们详细看一下每种加载策略的具体使用策略:

7.2.1懒加载

懒加载是当内容落入视窗被用户看到时,才会进行加载。这种比较节省资源和减轻服务器压力。对于当前页面内容很多的可以采用这种加载策略。而这种加载策略的设计,能够让用户更快看到当前屏幕内的内容,减少等待时间。

比如苹果官网的加载策略就采取了这样的一种方式。我们可以看到右侧的资源只有在我们向下滚动出现在屏幕中时才会进行对应的加载,这样能够保证用户在进入网页第一时间看到首屏内容,并且用户几乎感知不到这种加载延迟。


7.2.2预加载

预加载是在页面空闲的时候就把用户即将用到的资源加载完存到缓存中,等用户需要使用时,通过缓存直接调用呈现。比如用户在看A页面的时候,就可以通过预加载来准备用户需要的B页面资源。当用户需要B页面的时候,立刻就可以呈现。

比如某些页面在实际中加载比较慢,这个时候就可以考虑是否用预加载的策略来提升网页整体加载速度。比如知乎的视频和文字在都进行了对应的预加载。即使当你处于断网状态(图中我将页面网络切换为断开状态),可以看到依旧可以点击全文进行查看和视频的部分预览。

7.2.3分步加载

当页面中有文字和图片时,优先加载占用网络资源小的,比如文字资源,然后再进行占用资源较大的加载,比如图片资源。通过分步加载也能有效减少页面等待时间。比如大众点评等图片很多的网站往往会采用这种加载策略。

7.2.4分页加载

分页加载是我们目前很常见的方式,比如我们常用的百度和谷歌等搜索引擎都是使用的分页加载,分页适用于数据量比较大的内容,比如表格数据或者大数据搜索场景可以使用。

分页加载可以理解为当前获取到100条数据,但是将100条数据分别拆成5页每页20条数据提供给用户,这样也可以让用户减少等待时间:

在目前还有一种滚动分页的加载,就是不提供视觉上的分页,而是当用户进行滚动时,自动加载一定数量的内容,这样从用户的视角看就是一个连续不间断的数据展示。比如一些资讯类的网站就是采用的这种加载策略,有的也把这种滚动分页的方式称为自动加载。

7.2.5延迟加载

这里讲的延迟加载更多的是一种防护加载机制,一般延迟设置的时间为300ms。这里的延迟加载有2种使用,第一种多用于搜索,通过延迟加载能够让用户更好进行连续输入,避免搜索结果被高频率刷新,同时缓解服务器压力。如下图,可以看到我在输入1后会有个延迟才出现加载列表,并且我在连续输入12306的过程中是没有对结果进行更新的,当我输入完后才更新。

第二种则是通过延迟加载可以更好防止反复操作。当用户在同一组件上面进行切换时,如果该操作小于300ms,则只记录最后的点击操作。这种情况可以用在我们的表格翻页和开关等状态上,防止用户疯狂点击导致数据反复请求和屏幕闪烁的情况。我们可以通过下面这个组件演示例子来进行对应的感知:

延迟加载更多用于上述2种场景,对于其他情况的加载,直接加上就可以了,并不需要刻意设置延迟。

7.2.6后台加载

这里想要表达的含义是当用户在操作后,客户端立即反馈操作成功,然后把请求放到后台与服务器交互,这一过程用户不需要了解,不需要等待。无论在什么网络环境下你基本上都能操作成功。比如微信的朋友圈发布就是这样的操作,即使你在断网的情况下都能够立刻发布,但实际上这个时候并没有发送成功,还需要上传到服务器后才你的朋友们才能看见。

这样的好处是用户使用起来非常流畅,但是坏处就是,当操作不成功时,用户并不能及时进行感知,仍然以为操作已经成功了。所以这种场景我们也需要根据场景进行对应的判断和选择。对于复杂的B端场景个人建议慎用或者不用这种操作,毕竟很多发布的功能会同时影响很多业务线。

这里就拿微信的朋友圈发布来进行举例,下方手机状态都为断网状态,可以看到微信立即发布,而贴吧在这种状态下提示网络错误。

通过这些加载策略的选择性使用,能够在特定环境下提升我们系统的整体使用体验。因此我们需要对这些加载策略有一个比较全面的认识,这样我们不仅知道加载慢的原因,还可以利用加载策略去提升页面性能。

在整体的加载过程中,别忘了考虑加载异常的情况。在通常情况下我们会我们会遇到网络速度过慢或者突然断网这两种状况让页面一直加载不出来,这时我们需要考虑对长时间的加载过程进行处理。

通常做法是我们要对加载状态有一个最长时间的限制,一般为加载不超过10s,超过最长时间后就进行异常状态显示(提示语+刷新按钮)。这样用户可以选择重新加载或者离开此页面,而不是一直等待。

在这里还想到一点,就是对于编辑页面,我们应当加入网络连接是否异常的判断,比如当进入到编辑页面后,如果遇到网络断开,需要在页面的顶部加上常驻提示条【当前网络连接断开】,这样用户能够及时进行察觉并修复。避免在无网环境下继续输入。这对于某些需要输入很多内容且并不提供本地保存的页面来讲是非常有必要的。

加载在设计中是非常容易被忽略的模块,因为在大部分情况下网络速度都较快,人们很难深刻地感受到加载过程。但加载却在产品运行中起着不可或缺的作用。通过这篇文章想要告诉大家的有几个点:

1.我们需要明白加载为什么会出现,这个过程是怎么样的;

2.加载的通用处理手段是怎么样的,非通用处理方式有哪些;

3.通过缓存和对应加载策略能够让页面加载速度有本质上的提升。

这样当我们在页面上遇到加载速度慢的问题时,在为其加上动画承载过渡的同时,还应该思考其加载缓慢背后的原因,是因为数据量太大还是加载策略的相关问题,是否可以将其进行懒加载或者分步加载,这时我应该去找前端或者后端如何沟通。从源头上解决加载时间长的问题,才是后续产品设计过程中的长久思路。

最后,虽然进行了很多资料查询和技术沟通,但文章不可避免会出现不当之处,欢迎进行反馈。

参考资料:

[1]  「加载设计思考笔记」

[2] 「加载功能的原理和设计」 

[3] 「彻底理解浏览器的缓存机制」

[4] 「从输入url到页面加载完成的过程」

[5] 「浏览器渲染顺序」

[6] 「相关各种加载策略的解释等各种资料的支持」

Powered by Froala Editor