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

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

本文于2017年4月10日进行重大更新,之前阅读过本文的开发者请重新阅读。

窗体切换白屏的现实问题

\n

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

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

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

\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起,为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

在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目前编程方式还比较复杂,HBuilder alpha中提供了nview模板,可以使用类vue的方式开发。等成熟完善后会发布到正式版中。

延伸:既然有nview,那是不是可以使用nview做完整界面,废除webview?
DCloud一直遵循的是HTML5+的规范和理念,即不推翻HTML,而是在HTML做的不好的地方强化补足。
从技术角度,除了原生title,开发者当然在动画时扩大nview的区域甚至整屏nview,这样动画时可以迅速显示整屏内容。但DCloud认为这也仅限于动画时这一屏幕,完整的页面仍然应该是HTML的。

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

- 2、预加载html

\n

既然HTML渲染慢,是否可以后台预加载,需要使用时直接动画进来?
当然是可以的。所谓预载,即后台预载新页面的HTML文件及资源,使用时直接调出这个已经创建好的webview。
Hello mui、36kr等项目源码,都使用了预载思路。

在Hello mui里有一个列表到详情的最佳实践示例,就是使用了这个思路,建议大家在列表到详情时研究使用这个示例。文章参考http://ask.dcloud.net.cn/article/12575

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

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

-3、预截图(不推荐)

\n

一般情况有上述几种方法就够了。但预截图的能力HTML5+也是提供了的,理论上也可以使用,但有点奇技淫巧的感觉,不推荐使用。
5+ runtime提供了一个plus.nativeObj.Bitmap的对象,同时webview对象提供了一个截图方法,可以把webview显示区域保存到bitmap对象中。此外webview的动画方法中支持传bitmap,这样我们可以预载一个webview,然后把这个webview预先截图下来,然后在窗体移入时在动画参数里传入保存这个截图的bitmap对象,这样窗体移动时,移动的就不是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();

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

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

\n

plus.webview提供了很多切换动画,上下左右平移、淡入淡出、缩放、挤压......但比较常用的动画是右移slide-in-right和挤压pop-in。
一般在iOS上,强烈推荐使用pop-in,更接近原生体验。
在Android上,pop-in体验好,但性能消耗大于slide-in-right。因为pop-in是需要前后2个webview一起做动画,而slide-in-right是只有一个webview在动画。pop-in为了达到更好的效果,需要开发者编码时注意一些写法,如果写不好,效果还不如slide-in-right
这里是pop-in动画使用注意:http://ask.dcloud.net.cn/article/225

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

\n

有些人认为HTML5=b/s,所有页面都是要从服务器下载。
但原生App其实界面是本地的,联网从服务器只是取json和图片,数据量小。
这种差别引发的问题就是,c/s的app加载速度快于b/s网页,流量消耗小于b/s网页。
如果开发者是重写做一个app,一般推荐使用c/s模式,减少用户流量、加快切屏速度。
DCloud同时提供了c/s的本地app的动态升级方案,相比原生App,5+App的动态更新可以做到自动化、差量化。具体见http://ask.dcloud.net.cn/article/199
如果开发者已经有了一个M站,想要快速转换为App或流应用,这种bs网页要想加载快,只能使用subnview。

后记

\n

不管使用哪种方法,都要注意一点,手机App的HTML页面必须本身性能足够高。
页面体积要小、加载和渲染要快。
互联网上有很多提升HTML、JS、CSS性能的方案,此处不再罗列。
但再次强调,尽量避免使用js渲染页面。
在页面load那1、2秒钟,手机的压力是非常大的,加载众多资源、构建dom、渲染、还要做动画,此时大量依赖js渲染界面的做法会让手机cpu、gpu忙不过来,要不卡动画、要不阻塞js。
pc上web框架的盛行,也是后来pc浏览器性能足够高之后的事情,互联网发展初期的开发者并不像如今这般依赖框架。
手机,尤其是低端Android机的性能也很差,如果照着写pc web的思路写页面,最终的用户体验必然会非常差。
首先,AMD框架尽量不用,包括angularjs在内,js动态解析标签再替换渲染是很慢的。
其次,jquery也尽量不要使用。document.getElementById("") 、document.querySelectorAll("")、$(""),这三者性能依次下降。
并且HBuilder提供了很多代码块来快速完成代码,比如敲dg就可以出document.getElementById(""),比敲$("#")要快多了。
当然个别页面为了使用一些现成的jquery插件而引用了框架,倒也不会对app整体产生太大影响,这需要开发者自己根据产品对性能追求的极致程度来把握了。


103 分享
旧情 小小雨 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
822259889@qq.com

822259889@qq.com

文档天天都在看,看DCloud文档总有种莫名的感动,字里行间对思维的模式,思路的整理,能感觉到的用心。
0 赞 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

要回复文章请先登录注册