MUI体验框架
1. mui简介
1.1 缘起
1、基于jq的jqmobile,性能低的无法忍受,且UI难看
2、bootstrap这种响应式设计,性能在低端机不足,而且UI风格一看就是网页,不是App的感觉。
3、framework7当时只能在iPhone上运行也无法被接受。
4、基于angular的ionic,把pc端很重的东西引入到移动App中,且angular学习门槛较高
看来看去ratchet框架还比较接近我们想要的高性能App框架,但发展理念和我们不同,我们是要求极致化的考虑App的性能。因此,我们撸起袖子,基于ratchet大幅改造实现了第一版的mui。
发展了一年半,目前mui已较完善,基于mui的app数量可统计的有20多万了,在360、大众点评、网易、京东等公司都有App使用了我们的框架。mui在github上的star数看起来不多,是2500+,希望今天过后这个数字能大幅上涨☺
附上mui的地址:https://github.com/dcloudio/mui
1.2 Mui的特点
小巧
mui不依赖任何第三方js库,核心js只有几十K;mui的核心是一个ui框架,并不是一个JS库,因此mui在实现上有所为、有所不为;mui框架中的js代码,均是为了ui组件服务,没有常用DOM操作等封装,这是mui和jQuery的区别。另外jq等框架是因为ie6-11的兼容性问题而壮大起来的,手机上都是webkit,没有引入jq的必要性,原生js很好用;
原生UI
mui的ui设计理念是:以iOS为基础,补充Android平台特有的控件;因此mui封装的控件,ui上更符合app的体验。
高性能
这是mui的重要特点,后续会单独讲述。
丰富模板
mui在逐步解决性能问题的基础上,开始封装了一系列的ui模板,比如首次启动欢迎页面,这个功能通过mui做的话可能几行代码就出来了;
如下为手机通讯录模板,常用于是通讯录、城市选择等场景,支持右侧滑动字母快速选择。
2. mui框架如何解决性能问题
对于前端性能优化,大多数方法论是基于web的,比如压缩js、css,减少带宽请求;合并文件,减少网络请求次数;我们不谈这些常规的基于web的优化技巧,单独说说针对app,mui是如何实现性能优化的。
2.1 卡头卡尾及快速回弹滚动
在native app中,内容区域和顶部标题栏是不同的组件,内容区域的滚动条不会透穿标题栏;但使用HTML5开发mobileapp时,默认的body滚动条会透穿标题栏,这样的UI不符合app体验;通常的解决方案是使用DIV滚动(区域滚动),但DIV滚动是个大坑,我们简单回顾一下其历史:
•最早版本:iOS/Android均不支持非body元素的滚动
•iOS 5.0版本:支持区域滚动,滚动条可见
•Android 4.0:支持区域滚动,滚动条可见
•Android 4.1.x:支持区域滚动,滚动条不可见
•Android 4.4:支持区域滚动,滚动条可见
虽然区域滚动在不同版本上有各种问题,但body的原生滚动却一直很流畅; 因此我们的解决思路是:
• 将标题栏和内容区分别放到2个webview中
• 内容区使用body原生滚动;
Hello mui的首页其实就是index.html加list.html合并而成的,如下图:
为了方便工程师开发,我们封装了mui.init(subpages:{})方法,可以快捷创建子webview。
这里要引入一个概念,就是mui可以调用原生加速。在App开发中,js代码是和hybrid引擎一起打包成apk、ipa发布的,不是运行在浏览器里,此时引擎的原生操作能力要被充分发挥出来。
mui就是利用对原生的webview的操作能力,实现了在App中更流畅的体验。当然mui在普通浏览器里也可以运行上面的代码,这个是mui如何多端发布,后续会专门讲。
2.2 切页白屏和转场动画
传统的web开发,点击href跳转,会立即显示一个空白页面,然后看到页面的渲染绘制过程,这就是常说的“白屏等待”现象,这个是不符合App体验的;为了解决这个问题,部分框架推出了SPA(Single-pageapplication)方案,在一定程度上缓解了这个问题。
但SPA模式也有其短板:
1、DIV的动画模拟页面切换,在页面复杂时,会出现卡顿现象;
2、通过JS频繁操作DOM,VIEW切换时动态添加、删除事件监听,在低端机上会经常碰到性能问题,甚至出现浏览器崩溃。
MUI针对切页白屏的解决方案:
1、预加载和预渲染新界面,在背景创建webview加载新页面,点击跳转时,直接显示之前创建好的webview,因为已提前加载渲染,因此显示时不会白屏;
2、显示动画使用原生view动画,避免DIV动画的卡顿情况;
3、如果觉得还不够好,mui还提供预截图API,提前对预加载的webview进行截图(预截图),页面切换时直接使用截图代替webview的移动,毕竟图片移动动画的资源消耗要远低于view的移动动画资源消耗。
2.3 下拉刷新
DIV模拟的下拉刷新为什么会卡顿?在这里先带大家复习一下web页面的渲染过程,如下图:
主要流程:
1、JS代码执行:比如设置元素的位置属性;
2、样式计算:根据CSS选择器,对每个DOM元素匹配对应的CSS样式
3、布局:计算每个DOM元素最终在屏幕上显示的大小和位置。web页面中元素的布局是相对的,因此一个元素的布局发生变化,会联动地引发其他元素的布局发生变化,特别是其子元素和孙子元素
4、绘制:本质上就是填充像素的过程。包括绘制文字、颜色、图像、边框和阴影等,也就是一个DOM元素所有的可视效果。一般来说,这个绘制过程是在多个层上完成的
5、渲染层合并:由上一步可知,对页面中DOM元素的绘制是在多个层上进行的。在每个层上完成绘制过程之后,浏览器会将所有层按照合理的顺序合并成一个图层,然后显示在屏幕上
知道了如上渲染流程,我们就不难理解为何DIV模拟实现的下拉刷新容易发生卡顿现象;随着手指的移动,JS不停更改拖动区域的top属性,然后不停引发后续的布局、重绘、渲染层合并;
mui的解决思路:
1、把一个页面拆分成两个webview,拖动的时候只拖动内容区的webview,拖动过程中webview位置变化,但webview内DOM元素位置不变,因此有效避免页面的布局、重绘操作;
2、拖动及回弹效果均采用原生动画实现,这就保证了原生的流畅体验。
2.4 硬件加速那些坑
很多同学都知道,若想动画流畅,需要启动硬件加速!但硬件加速也不能乱用。
mui最初是基于ratchet改造的,第一版本我们发现在Android手机上很容易出现闪屏或滚动卡顿的情况,后来排查代码,发现ratchet源码中有如下代码:
这样相当于将.content下所有元素全部设置了GPU加速,即使是一个没有任何动画的静态元素;我们删除该css定义后,Andriod性能大幅提升。
接着发现在列表数据较多时,iOS平台的侧滑菜单很容易crash,Android终端反而没问题;测试之后在需要执行transform动画的元素父元素之上增加如下定义,就解决了iOS的crash问题:
增加的这两行css定义解决了iOS的crash问题,却引发部分Android终端的transform不渲染,因此该样式还需修改成仅iOS平台生效。
2.5 click延迟及事件透传
300毫秒的由来
2007年初,苹果公司在发布首款iPhone前夕,遇到一个问题:当时的网站都是为大屏幕设备所设计的,不适合手机屏幕的阅读;为了解决这个问题,苹果的工程师们做了一些约定,这当中最出名的,当属双击缩放(double tap to zoom),具体方案:iOS Safari监听用户点击操作,在双击后准确地定位到页面主体文章,并将其缩放至适合比例展现。
那么问题来了,当用户点击屏幕上的一个链接时,浏览器并不能判断用户确实是要打开当前链接,还是要双击放大;因此,iOS Safari就等待300毫秒,以判断用户是否再次点击了屏幕;若有再次点击则触发双击放大;否则,触发单击逻辑,手机浏览器的点击300毫秒延时从此而生。
浏览器厂商的解决方案
优化这个延迟问题,浏览器也在积极采取措施,比如chrome通过meta禁用缩放:
<metaname="viewport" content="user-scalable=no">
<metaname="viewport"content="initial-scale=1,maximum-scale=1">
这个方案的最大问题是:完全禁用了页面元素缩放,当你想缩放显示图片时,就无法操作;
然后Chrome团队接着宣布:他们将在包含width=device-width或者置为比viewport 值更小的页面上禁用双击缩放。当然,没有双击缩放就没有300 毫秒点击延迟;这个方案虽妙,但毕竟只有Chrome支持,且有版本限制;iOSsafari除了双击缩放,还有双击滚动功能,双击问题存在,300毫秒就存在。
mui框架的解决方案
监听手机浏览器的touch事件,通过touch持续时间及移动距离来识别单击(tap)事件,当这两个值都小于特定阀值时,则触发单击(tap)事件。
•touch持续时间:touchend/touchcancel触发时间-touchstart时间
•移动距离:tohchmove事件中屏幕位置和touchstart事件中屏幕位置之间的直线距离
事件透传
问题描述:单击(tap)遮罩蒙版时,事件透传到蒙版下层的DOM元素上;比如Action sheet下方有一个<a href=“xxx”></a>链接,当点击Actionsheet控件时,会触发下层a标签的href跳转;
原因:还是300毫秒惹的祸,tap立即触发,很快关闭了遮罩层;但浏览器在300毫秒后才触发click事件,此时根据屏幕位置计算,event.target就刚好是下层的a标签,因此触发了a的默认行为href跳转。
mui解决方案:
关闭遮罩时,仅修改遮罩的透明度,并不真的关闭遮罩;等待350毫秒后,再真实关闭遮罩,这样300毫秒后,click事件触发时目标元素依然是遮罩层,从而避免click事件透传到下层的a标签;
2.6 更多底层API调用
除了webview、截图这些API操作,mui事实上可以调用Android和iOS的40多万原生API,在能力上基本与原生相同。包括拦截短信认证码、调用原生推送、扫描二维码都不在话下。
3. 多端发布
背景:大家刚才看到了mui在打包App时可以调用原生能力提升App体验,但有人会问,那岂不是无法在普通浏览器里运行了?所以mui引入了多端发布理念,通过另一种方式实现跨平台。
概念:一套基于mui的HTML5工程,通过框架的自适应和前端构建工具条件编译双管齐下,实现同时发行到iOSAppStore、安卓各大应用商店、普通手机浏览器、微信App、百度直达号和360流应用,并且并且,在每个平台上都能调用该平台的专有API达到原生体验。
Mui框架本身是支持多端发布的,Hello mui是一个WAP、流应用、Android app、iOS app一套代码多端均可使用的示例,可参考Hello mui的代码,也可以直接访问http://www.dcloud.io/hellomui体验。
实现方案示例—打开新窗口:
使用过mui的同学知道,开发App时,使用mui.openWindow()方法会创建或显示一个新的webview;但在WAP或微信中,没有webview窗口,如何实现页面切换呢?此时mui会自动降级变成href跳转;
实现方案示例—子窗口:
为了流畅的滑动体验,mui不推荐使用DIV区域滚动,若有长列表时,建议使用mui.init(subpages:{})创建子webview,然后在子webview中使用原生滚动;同样的问题,为了兼容WAP和微信,mui在这两个平台,会计算位置并创建iframe,将子页面内容显示到iframe中。
之所以引入构建工具做条件编译,而不是全部在框架里动态判断,是因为把判断放在用户操作之前,能大幅减少用户等待。
总结下与纯SPA框架的对比。
mui不排斥SPA,在mui里我们也适度引入了SPA,但对比其他纯SPA的前端框架,mui的设计思路是与他们不同的。纯SPA也能开发一次到处使用,但无法在App环境下达到原生级的体验。Mui的多端发布,同样也是跨平台,但在执行性能、充分调用引擎能力上都有更强的优势。
4. DCloud还为HTML5开发者提供那些产品服务
4.1 HBuilder
Hbuilder是DCloud提供的极客型HTML5开发IDE,它应该是目前写前端代码最快的编辑器,它的AST语法分析和提示系统也是世界级的。
工具是免费的,没有增值收费项目。
4.2 流应用
流应用是我们做的一个很酷的、有突破性的东西。
一般的App在应用市场下载时是下载按钮,点击后需要一分钟左右、点击数次,app才能启动。但流应用是可以直接秒开的。
流应用,其实是把使用mui开发的、原本打包为apk发行的那些文件,压缩和改造了包格式,使得安装包可以边用边下,一个流应用一般4秒左右就可以安装启动。
见下图,在360手机助手里搜索大众点评外卖,可以看到下载按钮变成秒开按钮: