杜燕忍,葛华勇,王龙
(东华大学 信息科学与技术学院,上海 201620)
摘要:Lizard是针对目前前端开源框架中存在的不足,由携程研发出的适用于无线移动端开发的框架。Lizard Web App是运行在浏览器上的单页应用,所有加载资源都放在一个html文件上,所有的用户交互都交给JavaScript,所有的通信都交给Ajax。该文基于Lizard框架实现Hybrid App(混合模式移动应用)的开发,并针对框架中本地存储方式进行改进,由此提出了扩展store的存储方式。以多态方式封装store,由LocalStorage、IndexDB、SQLite by App替换cookie,进而支持多种本地存储方式,该文只是针对其中一种方式即LocalStorage进行封装,以提高携程业务开发效率。
关键词:Lizard;Hybrid App;JavaScript;Web App;HTML;Ajax;数据存储方式;无线移动端开发
0引言
近年来国内外对Native App取得了很多重要的研究成果,但是对Hybrid App研究还比较少。混合模式移动应用(Hybrid App)是指介于Web App与Native App这两者之间的App[1],它虽然看上去是一个Native App,但只有一个UI WebView,里面访问的是一个Web App,它具有Native App良好用户交互体验的优势和Web App[2]跨平台开发的优势。
随着HTML5[34]可通过手机浏览器运行,而不直接依赖于手机操作系统,并且现在各大手机浏览器均支持HTML5[5];Mobile phone中框架的发展(例如PhoneGap)也为Web App的发展提供了契机。主流的Hybrid方案分成三种:以PhoneGap[6]为代表,以WebView作为用户界面层,以Javascript[78]作为基本逻辑,与中间件通信,再由中间件访问底层API的方式进行应用开发;以Titanium为代表,通过对开发者提供友好的开发工具,并折中地把这种开发语言转换成原生语言,最终打包出整个应用;以Three20为代表,在开发原生应用的基础上,嵌入WebView,但是整体的架构使用原生应用提供。在携程无线[9]的Hybrid框架中,一开始就选择了PhoneGap方案,未来携程App会成为一个平台。
本文基于Lizard框架[10]实现Hybrid App的开发,针对原有框架中的本地存储方式和第三方框架的引用进行改进和优化,并对改进的方法进行实际项目开发,结果与改进之前的结果一样符合开发项目的要求,验证了本方法的有效性。
1Ctrip Wireless H5整体架构图
H5整体架构图如图1所示。由整体框架可知,无线H5基础框架主要包含以下部分,各业务模块需按要求对以下部分进行统一引用。
(1)第三方框架:zepto、backbone、require、understore等需要统一引用。
(2)核心框架:Common.js,提供最基础的UI、网络通信(ajax)、数据存储(cookie)等基类,以及UI组件、Widget组件、HybridAPI等组件,需要统一引用。
(3)H5公共基础样式:Main.css,提供最基础和公共样式,需要统一引用。
(4)H5公共业务组件:各类通用的业务组件,比如常旅客、常用地址,需要统一引用。
(5)H5公共业务模块携程(登录/注册)、支付等业务模块,需要统一引用。
2Lizard框架
2.1框架简介
Lizard框架架构如图2所示,Ctrip.H5.Lizard主要由4个模块构成:第三方框架、核心框架、公共组件库、业务组件库。
(1)第三方框架:Ctrip.H5.Lizard底层依赖的第三方库有4个:Zepto、underscore、backbone和Fastclick。
在Mobile端,Ctrip.H5.Lizard会加载Zepto,在PC端考虑到兼容性的问题,IE内核浏览器会采用Jquery,Ctrip.H5.Lizard可以做到对环境自适应进行加载。
Backbone在Ctrip.H5.Lizard是被定制的,其MVC框架中的Model和Controller的内容根据Ctrip.H5.Lizard适用的环境被复写。
Ctrip.H5.Lizard抽取了Fastclick的核心代码,复写了移动端的click事件。
(2)核心框架:Ctrip.H5.Lizard实现了Web App的基础功能。
在Javascript[1114]中实现了面向对象编程(OOP)的基础,封装了Ajax请求,处理了本地环境下的跨域访问,并对请求做了缓存处理,通过监控Hash的变化,实现View的无缝切换。
(3)公共组件库:Ctrip.H5.Lizard的公共组件库涵盖了UI组件库和Widget组件库两个模块组。
UI组件库以特定的样式表为基础,定制Ctrip Mobile的UI组件,实现了各种交互电话、时间选取等,Widget组件库提供了功能性组件库和UI业务组件库。
(4)业务组件库:Ctrip.H5.Lizard的业务组件库主要包括携程(登录/注册)、支付等业务。
2.2Lizard算法流程
Lizard算法流程图如图3所示,Lizard渲染数据共分成5个阶段:T1~T5。T1:页面响应时间。此时,用户通过浏览器发起一个请求,当前VIEW的HTML文件返回index.html给浏览器,浏览器接收到index.html开始解析,之后浏览器请求核心框架JS处理。Lizard.js收到请求,此时进入T2阶段,即数据通信请求阶段。Lizard.js返回核心框架中的JS文件给浏览器,浏览器收到核心框架中JS传来的数据,开始解析Model Config,根据Config中的API请求SOA数据,SOA收到浏览器的数据请求,此时进入T3阶段,即数据通信接收阶段。SOA返回数据给浏览器,浏览器接收到SOA的数据,此时T3阶段完成,T4阶段开始,浏览器根据接收到的SOA数据,开始解析Template,此时DOM生成,但是其为隐藏状态,此时浏览器继续发送请求给核心JS框架,T4阶段完成。此阶段又可以称作DOM稳定阶段。JS中的核心框架收到浏览器的页面逻辑请求开始进行处理,此时进入T5阶段,即页面显示阶段。JS返回处理的结果给浏览器,浏览器收到结果显示DOM,展示页面,T5阶段完成。其中T1~T4又称作DOM准备阶段,T2~T3称为通信准备阶段,整个T1~T5又叫做Onload阶段。Onload是Lizard渲染页面阶段中的View生命周期的一部分。
3Lizard Web App 中View的生命周期
首先,View的生命周期与携程无线Hybrid密不可分,图3Lizard算法架构在一个Hybrid架构的页面会有两部分组成:Head和Content。Head是由App提供的Android/IOS原生组件,对应IOSNavigationBar/AndroidActionBar。Content由App提供的WebView加载H5提供的页面,H5与App约定将一组API绑定在WebView的Window对象中。App通过IOS/Android原生方法调用到Window对象,执行js的方法,这称之为App Native调用Web。App实现对WebView URL的观察者模式,H5通过改变URL的哈希值,App会通过解析哈希值的变化执行对应的操作,这称之为Web调用App Native。Lizard支持Hybrid模式,而Lizard在对Hybrid的设计中,采用了切面编程的思想。Web与Hybrid最后的产品差异就在于入口文件,在入口文件中,会去做初始化,判断当前的环境,然后以当前环境作为参数,配置相应的设置,切换各种服务在不同环境下的配置。所以从开发者的角度来说,不用关心当前的环境是Web还是App,只需要调用相应的服务即可,剩下的事情交给Lizard自身就可以。
其次,Lizard中页面的整体布局代码如下:
define(['libs', 'cBasePageView'], function (libs, BasePageView){
var viewhtml = '<h1>Hello World</h1>';
var View = BasePageView.extend({
onCreate: function () {
this.MYMel.html(viewhtml);
}
onLoad: function () {
this.turning();
}
onShow: function () {
}
onHide: function () {
}
});
return View;
});
在该代码中显示了View的4个生命周期回调顺序。
onCreate -> onLoad -> onShow -> onHide
(1)onCreate
onCreate是只有在view生成时才会调用的回调。在这个回调中,一般处理View的模板构建、初始化数据设置。onCreate只会在初始化时候执行一次,第二次访问不会执行。
(2)onLoad
onCreate每次调用View时都会首先调用的回调方法。在该回调中需要处理数据请求、View的数据绑定、渲染和View的交互逻辑设置。onLoad的回调中需要执行this.turning()才会宣告view的加载完成,进入到下一个生命周期onShow,onLoad在每次切换View时都会执行,一般在onload中进行Ajax异步数据请求,请求suceess后调用turning方法从而触发onShow事件。
(3)onShow
onShow是新DOM完成渲染之后进入页面的回调,部分依赖页面渲染之后的业务逻辑可以绑定在这个回调中,onShow的执行时机为调用turning之后,不调用turning不会执行onShow。
(4)onHide
当页面执行了从A页面跳转到B页面时,A页面会执行hide方法,将View A推到后台。onHide就是在A推到后台之后的回调。
4改进数据存储方式
在一个应用中数据的存储是非常重要的功能,在携程的应用场景中,大量基础数据会被缓存下来,比如城市列表、用户使用历史等信息。在Web 2.0时代,Web存储呈现多样化的趋势,除了传统的cookie,HTML5提供了LocalStorage、SessionStorage、ApplicationCache等存储方案,同时Chrome还支持IndexDB。cookie操作在前端开发过程中经常遇到,当然如果只是用来存储一些简单的用户数据,还是比较容易的,要做的可能只是设置cookie名、值、过期时间等,读取也只要根据cookie的名读取相应的cookie值即可。HTML5本地存储的前身就是cookie,HTML5的本地存储是使用LocalStorage对象将Web数据持久化在本地。相比较而言,HTML5本地存储中每个域的存储大小默认是5 MB,比起cookie的4 KB要大得多。因此,HTML5本地存储可以看做是加强版的cookie,不受数据大小限制,有更好的弹性以及架构,可以将数据写入到本机的ROM中,还可以在关闭浏览器之后再次打开时恢复数据,以减少网络流量。同时,这个功能算得上是另一个方向的后台“操作记录”,而不占用任何后台资源,减轻硬件设备压力,增强运行流畅性,而且存储和读取数据的代码极为简练。因此在Lizard框架中采用了LocalStorage作为解决方案。同时参考redis在本地实现了小型的文档型数据库。
在store中针对LocalStorage封装了常用的方法,比如get(tag,oldFlag)获取已存储数据、getAttr(attrName,tag)获取已存储对象的属性、set(value,tag,{oldVal})向store中添加数据、setAttr(attrName, attrVal,tag)设置属性值、remove()移除数据存储、removeAttr(attrName)移除指定对象的存储属性、setExpireTime(time)设置过期时间、setLifeTime(lifeTime,override)设置当前对象的过期时间。
数据存储的整个过程如下:
(1)创建storage项目。
(2)cStore模块的引用:Lizard用cStore模块封装了LocalStorage的CURD操作,所以在使用数据存储时,需要引入cStore模块。Lizard采用require.js作为模块加载器,所以在使用时需要显示申明依赖关系。引用代码如下:
define([libs,cBasePageView,cStore], function(libs, BasePageView, Store){ ... });这里显示调用了cStore模块,在回调的参数对应的位置中给出store对象。
(3)Store的继承:Lizard实现了Javascript的OOP开发,为代码的复用提供了更好的机制。在Store的一般使用中,推荐将具体的Store继承于cStore模块,代码如下:
var StoreCase = new Base.Class(Store, {
__propertys__: function () {
this.key = STORAGE_EXAMPLE //设置在LocalStorage中的key值
this.lifeTime = 2D//D代表天,H代表小时,M代表分钟,S代表秒
}
initialize: function (MYMsuper, options) {
MYMsuper(options);
}
});
Store采用单例模式,所以需要通过StoreCase.getInstance()获得,即var storeinstance = StoreCase.getInstance()。
(4)Store的增删改读:首先设置view模板,其次进行绑定操作。Store提供了3个接口做绑定操作:读取Store.read(tag),设置/更新Store.set({...}),删除Store.remove()。
(5)设置数据的过期时间:对于过期时间的处理,在内部实现中参考redis的数据结构,本文设置了数据的过期时间。在LocalStorage中数据结构如下:
"value":{
"inputvalue":"asdfaf"
}
"oldvalue":{
}
"timeout":"2015/12/19 15:38:37",
"savedate":"2015/12/19 15:08:37"
通过对LocalStorage的封装,可以直接在开发时通过Lizard调用,使开发效率更快。
5结束语
基于Lizard框架的无线开发,是针对前端开发人员常用的一些方法、功能、组件等进行的一系列封装,提高了开发人员的工作效率。本文首先对Lizard框架的架构以及工作原理进行简单阐述,并针对Lizard框架中本地存储方式进行了优化,在store中封装了对LocalStorage的读写,实现了在Web上的本地缓存。其次,通过对LocalStorage的封装,cookie与store中的LocalStorage两种本地存储方式可以同时使用,更加方便。最后,正如文中所提到的一样,存储方式还有很多,诸如IndexDB、SQLite by App等都是以后技术发展的需要。
参考文献
[1] 万晓凤,雷继棠,易其军,等.基于Android智能手机平台的AMT监测系统的设计[J].电子技术应用,2014,40(3):6870.
[2] 朱华.AJAX在WEB开发中的应用研究[D].哈尔滨:哈尔滨工程大学,2009.
[3] 陶国荣.HTML5实战[M].北京:机械工业出版社,2012.
[4] cyRotel.WEB APP,HYBRID APP与NATIVE APP的设计差异[DB/OL].(2013219)[20150825].http://www.uisdc.com/webhybridnativeapp,20141202.
[5] 刘艳平,俞海英.基于HTML5的Application Cache技术研究[J].微型机与应用,2015, 34 (20):6466.
[6] PhoneGap.API介绍[EB/OL]. (20000118)[20160105].http://www.phonegapcn.com/docs/zh/cn/.
[7] NICHOLAS.JavaScript高级程序设计[M].北京:人民邮电出版社,2006.
[8] 徐浩,周德华,厂卓,等.面向多终端适配的移动中间件的设计与实现[J].计算机科学现代化,2011(9):6568.
[9] 刘妍.无线产品部的无线技术标准及指南[DB/OL].(20141208)[20160105].http://conf.ctripcorp.com/display/Wireless/3.HTML5.