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

提升HTML5的性能体验系列文章目录导航:
- [提升HTML5的性能体验系列之一 避免切页白屏]
- 提升HTML5的性能体验系列之二 列表流畅滑动
- 提升HTML5的性能体验系列之三 流畅下拉刷新
- 提升HTML5的性能体验系列之四 使用原生UI(nativeUI)
- 提升HTML5的性能体验系列之五 webview启动速度优化及事件顺序解析
- 提升HTML5的性能体验系列之六 降低内存占用

最后更新时间:2017年10月7日。

窗体切换白屏的现实问题

\n

HTML5的性能比原生差很多,比如切页时白屏、列表滚动不流畅、下拉刷新和上拉翻页卡顿。
在低端Android手机上,很多原生App常用的功能和体验效果都很难使用HTML5技术模拟。
我们首先来看第一个问题,如何避免切页白屏。

浏览器的页面在切换时,由于其页面加载机制,在跳转到下一个页面时,先要请求联网、载入页面代码、构建dom、渲染,最后才显示出来。
在最终结果渲染完毕前,会出现几十毫秒甚至数秒的白屏。原生App是没有这个问题的。
虽然使用SPA单页应用模型,即ajax+div切换也可以避免白屏,但把所有页面写在一个SPA页面里,简单几个页面还行。但页面多了手机上也跑不起来,初始化非常慢,首页必然白屏,而且工程大了代码那个乱。。。被坑过的人自然知道。

解决窗体切换白屏的方案

\n

标准HTML5无法解决,我们就使用扩展的手段。
HTML5+是一套增强HTML5的规范,它可以用JS调用几十万原生API。
想要解决切页白屏这个问题,需要使用plus.webview类来做MPA多页应用(不是SPA单页应用)。
plus.webview类是对原生的webview对象的js化封装,使用js可以操作webview。
解决白屏的原理是:把每个页面当作一个webview,但用js来控制它就像控制div一样。动画时通过原生的view动画飘webview进来而不是通过css动画飘div进来
同时webview之间相互独立,不会出现SPA下不同页面js和css冲突的问题。

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

1、动画先飘不会白屏的原生title进来

\n

既然webview加载慢,转场动画会白屏。原生view加载快,不会白屏。那么能不能使用原生view呢,或者至少动画时先飘一个原生view的title进来,也不会整屏白屏。
HBuilder7.2起,提供了plus.nativeObj.View,也就是原生的view对象(以下简称nview),可以使用js向原生的view直接写字、绘图(注意是原生view不是webview)。
从HBuilder8.8起,优化了nview和Webview的关系,为Webview引入了titleNView和subNView,是从属于Webview的原生界面。titileNView也称ntitle,进一步对title的原生化做了简化了操作,在plus.webview的style里,可以配置titleNView,如下示例:

plus.webview.create('new.html', 'new', {'titleNView':{'backgroundcolor':'#FFFFFF','titletext':'标题栏','titlecolor':'#FF0000'}});
\n

这样创建webview时,会自带一个原生的title,文字、颜色、是否有返回箭头、分割线这些都可以设置,见http://html5plus.org/doc/zh_cn/webview.html#plus.webview.WebviewTitleNViewStyles。还可以通过getTitleNView()方法得到一个nview对象,自由的向上面写字、绘图、处理点击响应。参考nview文档http://html5plus.org/doc/zh_cn/nativeobj.html#plus.nativeObj.View
如果只是简单修改,比如修改title文字,也可以通过重设TitleNView的style来实现:
plus.webview.currentWebview().setStyle({titleNView:{titleText:'new text'}})

在mui框架中,进一步简化封装了mui.openWindowWithTitle()方法,参考http://dev.dcloud.net.cn/mui/window/#openWindowWithTitle

上面title有了,中间空白处可以先转个plus.nativeUI.waiting的原生雪花或显示加载中,这样转场时就不会飘白屏了。
一般本地页面加载都很快,转场动画300毫秒结束时,页面也渲染出来了。

另外提供几个让HTML页面渲染快的方法
- 页面渲染尽量不用js做,想要渲染快,就直接写HTML和css渲染,js渲染的界面显示更慢。
- 少用padding、margin,尽量写简单的代码,让页面一次渲染到位,而不是反复触发重绘。
- 减少图片尺寸,不要使用背景图(最常见的性能问题均来自于此)

理解了titleNView,我们再来看subnview。
同理,subnview也是原生渲染的view,它可用于更大面积的原生渲染。
在流应用里的唯品会中,商品详情界面的加载速度那么快,就是因为使用了subnview。参考视频http://v.qq.com/x/page/k05051mc143.html
一般业务有titleNView就够了,追求极致体验的业务可以使用subnview。
所谓追求极致,就是要求在100毫秒渲染,动画期间就要完成联网和渲染。即使原生应用,大部分业务也是在动画完成后才渲染界面的。
使用subnview要在页面里大量通过js构造界面,不太直观。HBuilder8.3.3起,新增了wap2app项目,其中引入了nview模板,新建一个nview文件,可以使用类vue的方式开发,参考http://ask.dcloud.net.cn/docs/#//ask.dcloud.net.cn/article/12757。后续会把nview模板引入到5+应用开发中。

延伸:既然有nview,那是不是可以使用nview做完整界面,废除webview?类似react native那样。
DCloud一直遵循的是HTML5+的规范和理念,即不推翻HTML,而是在HTML做的不好的地方强化补足。
即使在动画期间大量使用了subnview,但滚动后的完整的页面仍然应该是HTML的。
这样的解决方案即能满足用户体验要求,又能兼容好HTML5,是更好的解决方案。

早些年mui曾推荐使用过head和body分开载入的方案,此方案已废弃,由这里描述的原生title方案替代。

2、预加载html

\n

既然HTML渲染慢,是否可以后台预加载,需要使用时直接动画进来?
当然是可以的。所谓预载,即后台预载新页面的HTML文件及资源,使用时直接调出这个已经创建好的webview。
尤其是从一个列表页面反复打开详情页面,仅仅是其中的数据不同,此时应该预载和复用详情Webview。
在Hello mui里有一个列表到详情的最佳实践示例,就是使用了这个思路,强烈建议大家在列表到详情时研究使用这个示例。文章参考http://ask.dcloud.net.cn/article/12575

只要服务器返回数据不拖后退,一样可以实现100毫秒渲染,动画期间完成联网和渲染。

预加载,由于不显示出来,并不会过多增加资源占用。(同时显示在屏幕上的webview不要超过3个,隐藏在后台的webview不要超过10个)
如果是list转到content,不同的item点击只是一个页面,完全可以使用预载。
但如果页面不同且较多,此时不建议预载太多Webview。后台预载太多webview需要耗很长,会抢cpu,此时用户如果在前台显示的Webview里操作比如滚动列表,会感觉到卡。

mui框架的窗体函数封装

\n

mui框架为了简化窗体管理的工作,把一些常用的窗体模型做了简化封装。
但对于复杂的窗体切换,仍需开发者搞明白上面提到的窗体切换原理。
mui的init方法,通过参数封装了preload,这样就可以方便的预载webview。
mui的openWindow方法,封装了显示waiting,载入新页面,处理动画,关闭waiting等工作。
mui的openWindowWithTitle()方法,封装了原生title。
mui的back样式控制,自动封装了窗体的隐藏和关闭。
这些方法具体参考mui的js API

启动后首页的白屏

\n

首页是没有预加载的概念的。
首页的控制基本都在manifest里进行。
有2个与启动白屏有关的manifest设置。
1.launchwebview
在launchwebview里可以配置首页的titlenview,以及使用subnview制作tab。
这样顶部和底部实际上是由原生引擎渲染的,可以迅速显示。
参考文章:基于subnview模式的原生tab


  1. splashscreen
    启动封面的图片如何关闭是在manifest里配置的。
    默认是在首页的webview的loaded事件发生后关闭。但又提供了若干选项。
    不管你的首页是白屏了还是觉得进入太慢了,都可以控制。
    在工程下manifest.json里找到plus、splashscreen节点,这里有event选项,可以配置是在哪个事件时close splash,默认是loaded,也可以配成titileUpdate、rendering、rendered。
    默认配置loaded事件是偏保守的,避免有的开发者首页代码写的不高效,导致白屏。
    如果你的首页代码效率高、渲染快,则推荐配置成titileUpdate事件。
\n

还有一种手动控制splash关闭的技巧,如果splashscreen节点下的autoclose设置为false,即手动,可以在首页代码里写js控制封面图片的消失时机。
此时在首页合适的位置,比如说联网结束或业务上的其他时间点,调用js关闭封面图片,plus.navigator.closeSplashscreen();
但不管什么方法,5+引擎的splash显示时间不会超过6秒,如果6秒内开发者仍不能做到首页渲染,那么用户会看到白屏。

关于如何优化启动速度,可以参考这篇文章http://ask.dcloud.net.cn/article/571

5+动画详解

\n

这篇文章详细描述了5+提供的各种原生动画的特点及优化技巧,是必读文章http://ask.dcloud.net.cn/article/225

Android5的动画花屏、分块渲染解决方案

\n

如果你遇到了相关问题,可以参考http://ask.dcloud.net.cn/article/12837

后记

\n

不管使用哪种方法,都要注意一点,手机App的HTML页面必须本身性能足够高。
这是老生常谈的问题,但现实中还是大量App因为这个问题而导致性能体验出问题。
编写干净整洁、一次渲染的页面非常重要。
现在太多开发者在研究模式、框架,让页面渲染要经历二次、甚至四五次重绘才能完成。在短短几百毫秒的动画期间,这么干要不让页面卡、要不让渲染慢。
减少js二次渲染很重要的就是减少使用js渲染页面,页面结构要用简单朴素的HTML写,js可以动态填充数据,但如果js动态绘制页面主体,那渲染速度上不去。
减少css二次渲染,就是少用复杂的选择器,少用padding、margin这些会二次修正页面的css。
如果追求极致的话,那jquery、zepto这些框架也不要使用,手机上都是webkit引擎,直接写document的api操作dom即没有兼容问题又没有效率问题。


110 分享
旧情 小小雨 M5 hellojh wglhtml5 炭烧咖啡 HU 海洋 JSoon 飞越 Cp0204 fany Solomon 毒气 太空小行星 Aylchen jayeeliu 太阳光 仙人指路 哈根达斯 Mr丶Leo 水粑粑 龙七 wuweitiandian 可怜的光头强 Liosixer 注水豆芽 xinglongjian 飞一扬 atubo 随风如下 damdmen 拉链里的小怪兽 junyi igo44444 木子喵 zgnm2009 Hzuy 趴趴熊 七星端砚 码牛666 涛涛江水 qmit 小指一弹 ccfto 蔡繁荣 Tronyel 永恒菂诺儿 tosmaller moliu
DCloud_Android_Yanbin

DCloud_Android_Yanbin 回复 Mislhy-j@qq.com

这个问题可参考下这篇文章:http://ask.dcloud.net.cn/article/110
其中的plus节点下的waiting属性控制的就是启动界面是否显示系统等待框,true表示显示系统等待框,false表示不显示系统等待框,默认值为false。
0 赞 2017-10-23 18:21
Mislhy-j@qq.com

Mislhy-j@qq.com

什么时候能够把安卓原生app+h5plus 混合开发模式下的io.dcloud.PandoraEntry 类处理下,让用户可以自定义app的欢迎页面,那是多么的美好,每次启动都在转圈圈,你们设计的时候考虑过吗?考虑过吗?考虑过吗?纠结了好几天,都快把百度和你们的文档翻完了,都没有找见有设置的地方,对得起我们开发者的心吗?难不成用户使用原生+H5集成的方式只能允许用户的app每次启动的时候转圈圈。。。你们不觉得很好笑吗?
0 赞 2017-10-16 21:40
DCloud_heavensoft

DCloud_heavensoft 回复 亿朵云

这个不是预加载了,5+引擎在同时打开多个窗体后,在Android上有2层的栈保留机制,超过2层的老窗体,渲染资源也会自动回收。除非开发者手动设置了Webviewstyle里的render:always,强制不出栈。按你说的情况,a、b出栈并回收渲染资源了,c的渲染资源并未释放
1 赞 2017-09-22 17:29
亿朵云

亿朵云

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

假设在页面A预加载了页面B、C、D,
那么由页面A mui.openWindow(B)
再由页面B mui.openWindow(C)
最后由C mui.openWindow(D)
显然,此时webview D是属于"显示在屏幕上的webview"
而mui.openWindow()并不会触发之前webview的hide事件
故此时
webview A、B、C是属于“隐藏在后台的webview”吗?
0 赞 2017-09-21 05:53
822259889@qq.com

822259889@qq.com

文档天天都在看,看DCloud文档总有种莫名的感动,字里行间对思维的模式,思路的整理,能感觉到的用心。
1 赞 2017-07-18 10:58
fq1798@163.com

fq1798@163.com 回复 好冷

也就是半年前,你是小白呗。
0 赞 2017-05-05 10:36
DCloud_heavensoft

DCloud_heavensoft 回复 377762305@qq.com

content页面里写一个Div的加载中啊。如果已经载过老内容,隐藏content时把老内容也清掉
0 赞 2017-04-18 19:17
377762305@qq.com

377762305@qq.com 回复 377762305@qq.com

我是用openwindow跳转至预载的页面的
0 赞 2017-04-18 18:26
377762305@qq.com

377762305@qq.com

以新闻类app为例,启动首先载入资讯列表list页面,然后后台创建了一个隐藏的webview,加载了一个内容模板content页面。
在点击list页面的一个新闻item时,调用webview的窗体控制动画,把content页面侧滑进屏幕。
但content页面仅仅是一个模板而没有数据,在content页面刚侧滑进屏幕时,在content页面有一个“加载中”的提示。
紧接着content页面开始执行ajax请求,联网加载数据并显示出来。

我想知道如何实现在content页面显示“加载中”,预加载是直接跳到content页面然后等待数据传进来,这样感觉很突兀,体验很差。
0 赞 2017-04-18 18:25
四阿哥

四阿哥

好文章!!
0 赞 2017-01-04 21:11
husheng

husheng

mark
0 赞 2016-12-08 16:41
513238368@qq.com

513238368@qq.com 回复 大鱼泡泡

百度一下你就知道,MPA多页应用程序,是multiple page application的缩写,SPA是单页应用程序,是single page application的缩写。
0 赞 2016-11-12 13:01
1968965852@qq.com

1968965852@qq.com

采用第三种加载head和body分开载入,在headWebview右上角弹popover会被bodyWebview遮住,怎么解决呢?
0 赞 2016-11-07 16:01
xwiwi@foxmail.com

xwiwi@foxmail.com

好东西 非常给力啊
0 赞 2016-10-19 14:50
824491253@qq.com

824491253@qq.com

灰常好,准备入手
0 赞 2016-09-19 10:45
505114230@qq.com

505114230@qq.com

受益匪浅,增加收藏功能就好了,先记录下以后应该能用到,谢谢作者
0 赞 2016-08-18 18:03
Fanta

Fanta

希望老板多分享一些js , css 性能提升的方案。----- “互联网上有很多提升HTML、JS、CSS性能的方案,此处不再罗列。”
1 赞 2016-08-13 14:10
1139330057@qq.com

1139330057@qq.com

mark
0 赞 2016-08-01 21:36
wfc1870

wfc1870

mark 记录下,备用
0 赞 2016-07-08 10:06
这个昵称真好

这个昵称真好

人不多啊 mark
0 赞 2016-07-07 17:54
臻厚

臻厚

学习了,性能就像一个拦路虎,很多放弃html5的人都是因为性能。
0 赞 2016-06-30 09:53
my87@163.com

my87@163.com 回复 大鱼泡泡

考虑下:http://www.thinksaas.cn/topics/0/380/380109.html
0 赞 2016-06-22 10:00
pioneer

pioneer

mark!
0 赞 2016-03-30 23:26
好冷

好冷

时隔半年,再次通篇阅读楼主文章,受益匪浅
1 赞 2016-03-16 08:50
szaos

szaos

必须mark
0 赞 2016-02-13 11:55
YL

YL

mark
0 赞 2016-01-05 11:34
ccfto

ccfto

~~~~~嘎嘎!
0 赞 2015-12-18 18:29
qmit

qmit

好文章,需要多领悟几次。
0 赞 2015-11-23 21:17
thinkphp_

thinkphp_

学习学习
0 赞 2015-11-10 15:24
lyndsey

lyndsey

顶一下
0 赞 2015-11-02 14:12
你不知道

你不知道

mark一下
0 赞 2015-10-20 10:36
拉链里的小怪兽

拉链里的小怪兽

想收藏下来,没找到地方……哦嚯嚯……
0 赞 2015-09-24 10:22
好冷

好冷 回复 DCloud_heavensoft

最终还是用的waiting转圈的方式,也许我没理解openwindow和create webview有什么区别

官方的文档里,只有waiting转圈的实例,小白,表示放弃,先用转圈凑合吧
0 赞 2015-09-20 21:45
DCloud_heavensoft

DCloud_heavensoft 回复 好冷

加载中这几个字是显示在main里的,刚开始webviewbody是隐藏的,虽然隐藏但也在联网取数据,渲染结束后把webviewbody再append到main上,盖住“加载中”这几个字。
当然这是众多窗体切换方式中的一种,具体使用哪种,还要根据业务情况决定。
0 赞 2015-09-17 02:12
好冷

好冷

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

这句话怎么理解?启动的时候登录页(login)预载main,main里有title和footer,然后body只是个“加载中”

login验证完了以后,显示main,并开始转圈?

body部分ajax获取数据插入dom结束之后,关闭转圈,显示body?

是这样么?
0 赞 2015-09-16 22:34
damdmen

damdmen

mark
0 赞 2015-09-11 20:56
lobtao

lobtao

非常不错
0 赞 2015-09-02 12:07
java_doc

java_doc

设置了webviewStyle对象里的render为always,为什么第一次打开子页面开始还是模糊,第二次打开就正常了?
0 赞 2015-08-07 17:06
张三丰

张三丰

mark
0 赞 2015-08-03 15:59
可怜的光头强

可怜的光头强

好文!
没法收藏,只能Mark一下。
0 赞 2015-07-27 21:16
大鱼泡泡

大鱼泡泡

文中讲到两个专业术语:MPA,SPA 可否详细说明一下,或者给个链接
0 赞 2015-07-23 09:24
通宵吃苹果

通宵吃苹果

已补学
0 赞 2015-06-21 11:52
DCloud_heavensoft

DCloud_heavensoft 回复 Aylchen

切换是避免做其他占用cpu的事情,比如载入webview。其实技巧在上面写了,如果还有问题,只能贴出代码看了。请单独发帖吧
0 赞 2015-05-29 05:37
Aylchen

Aylchen 回复 DCloud_heavensoft

如何破?求指教!
0 赞 2015-03-02 17:37
Aylchen

Aylchen

切换的时候会卡顿,感觉很不爽!
0 赞 2015-03-02 17:36
DCloud_heavensoft

DCloud_heavensoft 回复 zyhwyl

动画效果就是原生的view移动。卡或闪屏,是webview渲染的问题。要在写HTML时注意控制,避免渲染压力。
1 赞 2015-02-13 18:23
zyhwyl

zyhwyl

感觉切换的动画 和原生的还是有很大差距啊 。。有点卡 而且fade的动画会闪屏
0 赞 2015-02-13 18:17
Solomon

Solomon

觉得head和body分别加载比较好。
0 赞 2015-02-10 16:41
潘歌

潘歌

纠缠预加载和现载的问题
0 赞 2014-12-24 20:16
tkggusraqk

tkggusraqk

如果按照HTML样式布局生成原生对象,那就没问题了。
0 赞 2014-09-28 03:51
M5

M5

学习了。
0 赞 2014-09-26 09:03
小小雨

小小雨

mark...
0 赞 2014-09-17 23:52

要回复文章请先登录注册