夜鹰教程网-程序员的加油站
 当前位置:文章中心 >> Android/移动互联网/物联网/
提升HTML5的性能体验系列之一 避免切页白屏
夜鹰教程网 来源:www.yyjcw.com 日期:2017-10-25 16:11:47
提升HTML5的性能体验系列之一 避免切页白屏

系列文章目录导航:

- [提升HTML5的性能体验系列之一 避免切页白屏]

- 提升HTML5的性能体验系列之二 列表流畅滑动

- 提升HTML5的性能体验系列之三 流畅下拉刷新

- 提升HTML5的性能体验系列之四 使用原生UI(nativeUI)

- 提升HTML5的性能体验系列之五 webview启动速度优化及事件顺序解析


窗体切换白屏的现实问题


HTML5的性能比原生差很多,比如切页时白屏、列表滚动不流畅、下拉刷新和上拉翻页卡顿。

在低端Android手机上,很多原生App常用的功能和体验效果都很难使用HTML5技术模拟。

我们首先来看第一个问题,如何避免切页白屏。


浏览器的页面在切换时,由于其页面加载机制,在跳转到下一个页面时,先要请求联网、载入页面代码、构建dom、渲染,最后才显示出来。

在最终结果渲染完毕前,会出现几十毫秒甚至数秒的白屏。原生App是没有这个问题的。

虽然使用SPA单页应用模型,即ajax+div切换也可以避免白屏,但把所有页面写在一个SPA页面里,页面多了手机上也跑不起来,初始化非常慢,首页必然白屏,而且工程大了代码那个乱。。。被坑过的人自然知道。


解决窗体切换白屏的4种方案


标准HTML5无法解决,我们就使用扩展的手段。

HTML5+是一套增强HTML5的规范,它可以用JS调用几十万原生API。

想要解决切页白屏这个问题,需要使用plus.webview类来做MPA多页应用。

plus.webview类是对原生的webview对象的js化封装,使用js可以操作webview。

解决白屏的原理是:把每个页面当作一个webview,但用js来控制它就像控制div一样。

因为webview可以隐式创建,后台载入内容,并且在载入完毕时有js事件通知,我们可以在新页面载入完成后再把它通过动画移入屏幕,从而避免白屏。

同时webview之间相互独立,不会出现SPA下不同页面js和css冲突的问题。


通过操作webview来避免切页白屏,有几种常见的做法:


一种是称之为预载,即后台预载新页面的HTML文件及资源,使用时直接调出这个已经创建好的webview;

另一种称之为现载,即点击前页的链接开始走waiting转圈,同时后台开始加载完整的新页面,加载完再用js控制显示到前台。

还有一种称之为分开载入,随后会细讲。


- 1、预加载


所谓预载,即后台预载新页面的HTML文件及资源,使用时直接调出这个已经创建好的webview。

Hello mui、csdn、36kr等项目源码,都使用了预载思路。

以新闻类app为例,启动首先载入资讯列表list页面,然后后台创建了一个隐藏的webview,加载了一个内容模板content页面。

在点击list页面的一个新闻item时,调用webview的窗体控制动画,把content页面侧滑进屏幕。

但content页面仅仅是一个模板而没有数据,在content页面刚侧滑进屏幕时,在content页面有一个“加载中”的提示。

紧接着content页面开始执行ajax请求,联网加载数据并显示出来。

我们可以在list页面的item点击里,一边移动窗体,一边通知新页面执行ajax。webview间相互传递消息使用webview的evalJS方法。

这种做法,相当于用户是在新的content页面来等待联网数据。

示例代码如下:


var webviewContent;

document.addEventListener('plusready', function(){ //扩展的js对象在plusready后方可使用

    webviewContent = plus.webview.create("content.html");

});

function clicklist (id) { //list点击item后的事件

    webviewContent.show("slide-in-right",200);

}

在mui框架里,对这个过程进行了简化封装,使用preload参数来控制。参考:http://dcloudio.github.io/mui/javascript/#preload


预加载,由于不显示出来,并不会过多增加资源占用。(同时显示在屏幕上的webview不要超过3个,隐藏在后台的webview不要超过20个)

如果是list转到content,不同的item点击只是一个页面,完全可以使用预载。

但如果页面不同且较多,后台预载太多webview会有点慢。这里的慢不是会造成手机慢,而是预载是耗时间的。

Hello H5+这个示例使用了预加载技术,有一些网页在启动时就被预载了。点击到这些网页时,切换会非常快。

之所以不预载所有的网页,不是内存不够,是时间不够。

预载这些网页时手机CPU消耗比较高,此时如果滑动列表,会发现滑动不流畅。

为了避免这个问题,Hello H5+的首页是延迟进入的,等待预载的几个页面都完成后才调用plus.navigator.closeSplashscreen()关闭启动封面图片。

如果预载太多页面,等待全部预载结束后再进入首页,会导致App启动非常慢。


- 2、现载


所谓现载,就是用户点击时后台创建webview加载新页面完整内容,渲染后再显示到前台

有时我们无法使用预载,为了避免白屏,就要等待新webview完成后再通过动画把它移进来。

当点击list页面的item时,首先通过plus.nativeUI.waiting()来弹出一个等待框。

紧接着在后台create一个webview,载入content页面。

content页面在后台联网获取数据。

content页面在数据解析渲染后,有两种方式通知list页面。

一种是在list页面注册content页面的webview的loaded回调,在新webview载入完成后系统会自动触发loaded事件。

还有一种是在content页面合适的js位置通过evalJS方法通知list页面关闭等待框,并执行窗体切换把content页面显示出来。

当然content页面也可以不通知list页面,而是自己直接关闭等待框,并调用窗体切换动画把自己显示出来。

示例代码如下:


function clicklist (id) { //list点击item后的事件

    var nwaiting = plus.nativeUI.showWaiting();//显示原生等待框

    webviewContent= plus.webview.create("content.html");//后台创建webview并打开show.html

    webviewContent.addEventListener("loaded", function() { //注册新webview的载入完成事件

        nwaiting.close(); //新webview的载入完毕后关闭等待框

        webviewContent.show("slide-in-right",200); //把新webview窗体显示出来,显示动画效果为速度200毫秒的右侧移入动画

    }, false);

}

需要注意的是,为了减少窗体切换的等待,一般不在点击后使用webview的create方法,因为创建过程也有时间消耗。

比较好的方法是提前创建一个webview,需要载入页面时使用webview的loadURL方法来载入。


如果中间还是出现白屏,可以通过延迟显示新webview来解决。

目前官方演示demo里,把新webview移入进来是在新webview的loaded回调中做的,这个时机略晚于新页面的DOMContentLoaded时机,即dom tree完成但页面未必渲染完毕。

此时可以settimeout延迟新webview的显示动画,也可以干脆不在上级页面操作显示动画,而是在下级页面的onload或你认为页面已经渲染完毕的时间来操作把下级页面用动画显示出来。


现载的体验不如预载,一个app的常用页面都应该使用预载。不常用的页面受手机资源限制无法预载,才会使用现载。


- 3、head和body分开载入


了解以上两种方式后,我们可以玩些更复杂的。

一般窗体都是2部分组成,顶部的标题栏(带返回按钮)和中间的内容区,或称body区。

我们把它分成2个webview,一个叫webviewHead,一个叫webviewBody。(名字是随便起的,方便下面引用)

我们可以在首页先预载webviewHead,载入一个head.html的页面,这个HTML里上面是title内容+back按钮或其他菜单按钮,其中间body区只有一行字“加载中...”

然后再预载一个webviewBody,先放着不用。

在点击切换窗体时,首先用窗体动画直接把这个已经预载的webviewHead移入窗体内,此时用户会看到一个新界面,上面显示加载中字样。

同时js代码操作webviewBody的loadURL,给它载入你需要载入的页面,等待这个webview的loaded触发后,再把它append到webviewBody里。

这样,可以做到不预载,也能让用户尽可能的感受到窗体的流畅切换。

Hello mui的窗体切换,采用了这种方式。大家可以去看它的实现代码。

使用这种方式是要注意:head.html的body区域的背景色和webviewBody里载入的页面的背景色应该统一,这样在把webviewBody append到webviewHead时才不会突兀。

另外,head和body分开载入,对于body的显示会略有延迟。如果body是静态内容,在iOS上由于性能很好,HTML的页面切换比较快,其实head和body合并为一个页面直接载入会更快点。


-4、预截图


预加载可以避免白屏的发生,但窗体动画有时还不如预期流畅,有些新窗体移入过程中,还在不停联网获取数据,不停重绘界面,导致窗体进入过程感觉卡顿,此时还有一个高级技巧是截图动画。

5+ runtime提供了一个plus.nativeObj.Bitmap的对象,同时webview对象提供了一个截图方法,可以把webview显示区域保存到bitmap对象中。此外webview的动画方法中支持传bitmap,这样给开发者提供了一个性能调优的手段。

我们可以预载一个webview,然后把这个webview预先截图下来,然后在窗体移入时在动画参数里传入保存这个截图的bitmap对象,这样窗体移动时,移动的就不是webview,而是移动的图片,这样能让窗体动画流畅许多。

流畅度:飘图>飘webview>飘div。

一般场景下飘webview就够了,在特别追求极致的环境下,可以飘图,流畅度杠杠的。

从HBuilder7.2起,nativeObj下又扩展了view对象,可以写字、贴图,还提供点击事件。

举个实例:从商品列表到商品详情界面,为了让点击响应速度更快,我们完全可以把商品详情里的非数据部分预截图,从列表点击时先popin动画飘一张提前截好的图进来,同时在图上面把商品名称写上去,同时ajax联网加载商品的json数据并渲染,然后在关闭假图。


几种窗体切换方式的比较和适用场景


大家搞明白5+提供的这些api的原理后,其实可以灵活的根据自己的需求应用webview和nativeobj。

但我们还是归纳一下之前提到的3种切换方式适用的场景,当然我们也会提到SPA的适用场景。

1. 如果是类似于新闻资讯或公文列表等应用,每个列表item点击后打开的是同一个页面。应该使用预载的方式,直接preload,然后每次在这个content页面里面通过ajax请求服务器来更新数据。当然退出这个界面时记得要把ajax请求后渲染的内容hide掉,不然下次点另一条新闻会先闪一下上一篇新闻。

2. 如果是类似九宫格导航,或每个列表item去往不同的页面,如果页面数量低于9个,可以在启动时直接预载这9个webview。

如果页面太多,无法全部预载,建议把导航拆成二级导航。

比如带tab或segment的九宫,或者二级导航列表,在展开二级导航时做预载。

如果业务场景合适,还可以做智能预载,判断用户空闲时间和接下来可能要点击的页面来预载。当然高手才能搞定这么复杂的代码。

3. 如果页面因为各种条件无法预载,至少把能预载的预载一部分,剩余的按如下方式处理:

- 3.1 如果都是本地页面不联网的简单页面。

比如很多软件的设置界面,其内部的二级页面跳转,内容都很简单,也不涉及区域滚动卡的问题,此时SPA比较好。确实其实SPA也不是一无是处,它适用的场景下用它是比较好的。当然这么做虽然体验好,但一个App里一会MPA、一会SPA,一般开发者可能会头晕。那些追求极致体验的高级开发者可以通过混合MPA和SPA,博采众家之长来达到最佳用户体验。

而对于普通开发者,其实统一使用head和body分离的多webview方式其效果也完全可以商用。

在Hello mui里,下方有setting模板,就是一个spa的样例。在iOS上,这个样例的效果可以完全达到原生效果,顶部标题栏的渐变非常酷。

- 3.2 如果是本地页面不涉及联网但有下拉刷新或超长复杂图文列表,那还是head和body分开载入的方式比较好。

因为SPA的复杂div滚动和下拉刷新在低端Android手机很卡。

比如一些列表页面,本地有缓存的内容,从websql等本地数据库里加载内容,通过下拉刷新的方式更新数据,这类页面本身设计就应该是双webview的。

- 3.3 如果是联网获取内容的页面,使用head和body分开载比较合适

如果切一个页面,要等待新页面载入,新页面载入后还要等待联网载入数据,连续2个等待框就会让用户反感。

反正也是要等待联网,干脆就和等待webview加载一起等了。

先把预载的webviewHead移入屏幕,然后等待webviewBody载入HTML及联网获取数据,一并载入完毕后把webviewBody拍到主webview上。


mui框架的窗体函数封装


mui框架为了简化窗体管理的工作,把一些常用的窗体模型做了简化封装。

但对于复杂的窗体切换,仍需开发者搞明白上面提到的窗体切换原理。

mui的init方法,通过参数封装了preload和subpage,这样就可以方便的预载webview,对于head和body分离的双webivew界面,也可以方便的通过subpage参数来控制webviewBody。

mui的openWindow方法,封装了显示waiting,载入新页面,处理动画,关闭waiting等工作。

mui的back样式控制,自动封装了窗体的隐藏和关闭。

这些方法具体参考mui的js API。


Hello H5+和Hello mui的窗体切换比较


Hello H5+和Hello mui都是简单的本地静态页面,但页面数量非常多,导致无法全部预载。

所以其实很多实际的App可以通过预载实现比这2个示例App更好的窗体切换效果。

Hello H5+的转场策略是:

1. 预载一些常见的,比如主列表前2个webview,以及窗体切换和下拉刷新的webview。

2. 其他的窗体采用了“现载”的方式,即点击item后立即弹waiting,然后后台加载webview,载入完毕后移入屏幕。

Hello mui的转场策略是:

整体采用head和body分开载入的方式。

从直观的感受看2个示例,在iOS上,Hello H5+效果更好。在Android上,各有千秋。

但静态示例的设计和实际业务场景不同,由于实际业务大量存在联网内容,所以其实head和body分开载入更实用。

mui封装了head和body分开载入的模板设计,而Hello H5+只是api演示,不会做封装,所以造成2个示例的转场方式不同。

mui的窗体切换,这里有一篇单独的文章描述http://ask.dcloud.net.cn/article/106


启动后首页的白屏


首页是没有预加载的概念的。

启动封面的图片如何关闭是在manifest里配置的。

默认是在首页的webview的loaded事件发生后关闭。但又提供了若干选项。

不管你的首页是白屏了还是觉得进入太慢了,都可以控制。

在工程下manifest.json里找到plus、splashscreen节点,这里有event选项,可以配置是在哪个事件时close splash,默认是loaded,也可以配成titileUpdate、render。

如果autoclose设置为false,即手动可以在首页代码里写js控制封面图片的消失时机。

此时在首页合适的位置,一般在plus的ready事件后,调用js关闭封面图片,plus.navigator.closeSplashscreen();


关于HBuilder早期版本Android手机返回时页面会先模糊后清晰的处理方法


为了节约系统资源,在webview不可见时,我们的引擎默认会回收掉它的渲染资源(但js仍可以正常操作)。

如果页面复杂、渲染的慢,在返回时可能会因为来不及渲染而造成先模糊后清晰的问题。

此时或者优化页面写法,加快渲染。或者使用我们提供的api,使得webview在不可见时一样不移除渲染资源。

具体API地址见plus.webview的webviewStyle对象里的render参数,render设为always即可不移除渲染,解决模糊的问题。http://www.html5plus.org/doc/zh_cn/webview.html#plus.webview.WebviewStyle

另外从HBuilder7开始,pop-in、slide-in-right、fade-in等动画经过特殊处理,在返回时不会虚一下。


关于因Android硬件加速配置不当导致的闪屏、滚动不流畅、视频无画面等问题


5+runtime默认是开启Android硬件加速的,但Android的一些非google官方rom的硬件加速有bug。尤其是Android5.0初期的一些rom。

关于这方面的处理方案,单独起了一篇文章,参考http://ask.dcloud.net.cn/article/55


关于pop-in挤压动画和slide-in-right右移动画的取舍


plus.webview提供了很多切换动画,上下左右平移、淡入淡出、缩放、挤压......但比较常用的动画是右移slide-in-right和挤压pop-in。

一般在iOS上,强烈推荐使用pop-in,更接近原生体验。

在Android上,其实也是pop-in效果更好,但为了达到更好的效果,也需要开发者编码时注意一些写法,如果写不好,效果还不如slide-in-right

这里是pop-in动画使用注意:http://ask.dcloud.net.cn/article/225


关于b/s和c/s的切屏效果对比


有些人认为HTML5=b/s,所有页面都是要从服务器下载。

但App其实界面是本地的,联网从服务器只是取json和图片,数据量小。

这种差别引发的问题就是,c/s的app加载速度快于b/s网页,流量消耗小于b/s网页。

所以想要优化到App的体验,就使用正经的App开发模式,做c/s,减少用户流量、加快切屏速度。

DCloud同时提供了c/s的本地app的动态升级方案,相比原生App,5+App的动态更新可以做到自动化、差量化。具体见http://ask.dcloud.net.cn/article/199


后记


不管使用哪种方法,都要注意一点,手机App的HTML页面必须本身性能足够高。

页面体积要小、加载和渲染要快。

互联网上有很多提升HTML、JS、CSS性能的方案,此处不再罗列。

但注意一点,如非必要,不要使用框架。

pc上web框架的盛行,也是后来pc浏览器性能足够高之后的事情,互联网发展初期的开发者并不像如今这般依赖框架。

手机,尤其是低端Android机的性能也很差,如果照着写pc web的思路写页面,最终的用户体验必然会非常差。

首先,AMD框架尽量不用,包括angularjs在内,js动态解析标签再替换渲染是很慢的。

其次,jquery、zepto也尽量不要使用。document.getElementById("") 、document.querySelectorAll("")、$(""),这三者性能依次下降,尤其是在低端Android上遍历dom时,当你辛辛苦苦减少白屏和用户等待时间时,你会非常愤怒这些js框架拖了你的后腿。

并且HBuilder提供了很多代码块来快速完成代码,比如敲dg就可以出document.getElementById(""),比敲$("#")要快多了。

当然个别页面为了使用一些现成的jquery插件而引用了框架,倒也不会对app整体产生太大影响,这需要开发者自己根据产品对性能追求的极致程度来把握了。


复制链接 网友评论 收藏本文 关闭此页
上一条: 如何让手机访问PC网站自动跳转到手…  下一条: Android和IOS 字体该做多大合适?
夜鹰教程网成立于2008年,目前已经运营了将近 13 年,发布了大量关于 html5/css3/C#/asp.net/java/python/nodejs/mongodb/sql server/android/javascript/mysql/mvc/easyui/vue/echarts原创教程。 我们一直都在坚持的是:认证负责、一丝不苟、以工匠的精神来打磨每一套教程,让读者感受到作者的用心。我们默默投入的时间,确保每一套教程都是一件作品,而不是呆板的文字和视频! 目前我们推出在线辅导班试运营,模式为一对一辅导,教学工具为QQ。我们的辅导学科包括 java 、android原生开发、webapp开发、商城开发、C#和asp.net开发,winform和物联网开发、web前端开发,但不仅限于此。 普通班针对的是国内学员,例如想打好基础的大学生、想转行的有志青年、想深入学习的程序员、想开发软件的初学者或者业余爱好者等。 就业办针对即将毕业上岗的大四学生,或者打算转行的初级开发工程师。 留学生班针对的是在欧美、加拿大、澳洲、日本、韩国、新加坡等地留学的中国学子,目的是让大家熟练地掌握编程技能,按时完成老师布置的作业,并能顺利地通过考试。 详细咨询QQ:1416759661   夜鹰教程网  基于角色的权限管理系统(c-s/b-s)。
  夜鹰教程网  基于nodejs的聊天室开发视频教程
  夜鹰教程网  Git分布式版本管理视频教程
  夜鹰教程网  MVC+EasyUI视频教程
  夜鹰教程网  在线考试系统视频教程
  夜鹰教程网  MongoDB视频教程。
  夜鹰教程网 Canvas视频教程
  夜鹰教程网 报表开发视频教程
  热点推荐
在Struts 2中使用JSON Ajax支持
解决JSP中使用request乱码问题
JSP+JavaScript打造二级级联下拉菜…
Tomcat中文手册(1)
JSP及Servlet中遇到的多线程同步
详解:JSP和Servlet中的绝对路径和…
JSP Struts之HTML标签库详解
自定义JSP标签
JSP中的include指令
errorPage设置方法--JSP
JSP 国际化-格式化货币和日期
九个隐含对象使用总结JSP的
Tomcat中文手册(2)
tomcat6关于EL表达式的一个错误
jsp分页技术代码
  最近更新
iOS 关于退出键盘两种方法和避免遮…
安卓apk签名常见命令
把activity当成dialog使用
如何选择APP开发框架
Layout _width ,Layout_height和…
Android调用百度地图使用时出现in…
安卓刷机术语
android端实现断点续传功能
利用TCP/IP实现Android客户端与服…
Invalid project description ove…
推荐使用的meta标签
手机网站用Bootstrap还是jQuery M…
如何让手机访问PC网站自动跳转到手…
提升HTML5的性能体验系列之一 避免…
Android和IOS 字体该做多大合适?…

关于我们 | 网站建设 | 技术辅导 | 常见问题 | 联系我们 | 友情链接

夜鹰教程网 版权所有 www.yyjcw.com All rights reserved 备案号:蜀ICP备08011740号3