菜单

浅谈Hybrid技术之宏图和贯彻

2018年11月15日 - CSS/CSS3

浅谈Hybrid技术的计划性与落实

2015/11/05 · 基本功技术 ·
Hybrid

初稿出处:
叶小钗(@欲苍穹)   

浅谈Hybrid技术之规划以及贯彻

前言

乘势移动浪潮的起,各种APP层出不穷,极速的政工扩张提升了组织对开发效率的渴求,这个时节下IOS&Andriod开发一个APP似乎成本有点过大了,而H5的亚本钱、高效率、跨平台等特性即吃采用起来形成了一致栽新的出模式:Hybrid
APP。

作为同样种植混合开发之模式,Hybrid
APP底层依赖让Native提供的器皿(UIWebview),上层使用Html&Css&JS做作业支出,底层透明化、上层多多样化,这种现象酷有利前端介入,非常适合业务高速迭代,于是Hybrid火啦。

自我道这种支付模式既然大家都知道了,那么Hybrid就不曾什么探讨的价了,但叫我好奇的凡还发出广大人口对Hybrid这种模式感到陌生,这种情形在二线城市大宽泛,所以我此品尝从其它一个面为各位介绍Hybrid,期望对各位是的技巧选型有所帮助。

Hybrid发家史

头携程的运用全部凡是Native的,H5站点只占该流量大粗的同等有的,当时Native有200口盛,而H5开仅来5总人口左右当打酱油,后面无线团队来了一个执行力十分胜似的劳务器端出身的leader,他以打探前端开发,居然亲手使用jQuery
Mobile开发了第一版本先后,虽然高速方案就叫推翻,但是H5团队起发力,在缺少日内都遇到了Native的工作进度:

图片 1图片 2图片 3

出人意料发出一天andriod同事跑过来报告我们andriod中来一个艺术极其特别养限制,可能有页面需要我们内嵌H5的页面,于是Native与H5框架团队牵头做了第一独Hybrid项目,携程第一差面世了同一仿代码兼容三端的图景。这个开效率杠杠的,团队品尝到了甜头,于是乎后续的频道核心还开了Hybrid开发,到本人离开时,整个机制已经大秋了,而前者也发出几百人数了。

场面重现

狼厂有三大娘流量APP,手机百度、百度地图、糯米APP,最近连接糯米的时段,发现她们呢以做Hybrid平台化相关的放,将静态资源打包至Native中,Native提供js调用原生应用的力量,从产品化和工程化来说做的特别正确,但是出零星单短:


资源全打包至Naive中APP尺寸会增大,就算以增量机制为避免不了APP的膨大,因为本接的频道于少一个频段500K从来不感觉,一旦平台化后主APP尺寸会急剧增大


糯米前端框架团队包装了Native端的力,但是并未供配套的前端框架,这个解决方案是匪整的。很多作业就生H5站点了,为了接通还得单独开发同法程序;而即便是新工作过渡,又会面临嵌入资源要是静态资源的限量,做下的种尚未SEO,如果关注SEO的言辞或要再次付出,从工程角度来说是出题目的。

可是自活只是连入度与产品化来说,糯米Hybrid化的百般方向是生开朗的,也真赢得了一部分成就,在缺少日即发生成千上万频段接入了,随着推广进行,明年说不定会见形成一个特大型的Hybrid平台。但是盖自己呢涉了推广框架,当听见他们忽悠我说性能会加强70%,与Native体验基本一致时,不知怎么我甚至笑了……

总结

假若念了点几乎个故事你仍不亮堂为什么要使用Hybrid技术以来,我这边更开一个总结吧:

JavaScript

Hybrid开发效率高、跨平台、底层本
Hybrid从作业开销及说话,没有版本问题,有BUG能就修复

1
2
Hybrid开发效率高、跨平台、底层本
Hybrid从业务开发上讲,没有版本问题,有BUG能及时修复

Hybrid是发缺乏点之,Hybrid体验就必定没有Native,所以下产生其场景,但是于需迅速试错、快速占领市场的团队来说,Hybrid一定是勿次的挑选,团队生下来后要得开经验更好之原生APP

吓了,上面拉了那么基本上没因此的事物,今天之目的其实是也大家介绍Hybrid的一对计划学问,如果你认真阅读此文,可能于偏下地方对您有着助:

① Hybrid中Native与前者各自的办事是什么

② Hybrid的相互接口如何计划

③ Hybrid的Header如何设计

④ Hybrid的如何统筹目录结构以及增量机制如何促成

⑤ 资源缓存策略,白屏问题……

文中是我个人的一些开经历,希望对各位有因此,也冀望各位多支持讨论,指出文中不足和提出您的有的建议

接下来文中Andriod相关代码由自身之同事明月供,这里特别感谢明月同学对我之支撑,这里扫描二维码可以下载APP进行测试:

Andriod APP二维码:

图片 4

代码地址:

https://github.com/yexiaochai/hybrid

前言

随着活动浪潮的兴起,各种APP层出不穷,极速的事情扩充提升了团伙对开发效率的求,这个时刻以IOS&Andriod开发一个APP似乎成本有点过高了,而H5的不比本钱、高效率、跨平台等特点即被用起来形成了平等种新的支付模式:Hybrid
APP。

作一如既往栽混合开发的模式,Hybrid
APP底层依赖让Native提供的容器(UIWebview),上层使用Html&Css&JS做政工开销,底层透明化、上层多多样化,这种情景酷有利前端介入,非常适合业务快速迭代,于是Hybrid火啦。

当我道这种支付模式既然大家都知了,那么Hybrid就从未有过啊探讨的值了,但令自己惊奇的凡仍有成千上万人口对Hybrid这种模式感到陌生,这种状况以二线城市酷宽泛,所以自己这边品尝从外一个点于各位介绍Hybrid,期望对各位是的技能选型有所助。

Hybrid发家史

头携程的运用全部凡Native的,H5站点只占其流量异常粗之等同组成部分,当时Native有200丁盛,而H5开仅发生5总人口左右于打酱油,后面无线团队来了一个执行力十分强之服务器端出身的leader,他为打探前端开发,居然亲手使用jQuery
Mobile开发了第一本子先后,虽然很快方案便给推翻,但是H5团队始发发力,在缺乏日内已经撞了Native的事务进度:

图片 5图片 6图片 7

陡发生一天andriod同事跑过来报告我们andriod中生一个道极其可怜培训限制,可能部分页面需要我们内嵌H5的页面,于是Native与H5框架团队牵头做了第一单Hybrid项目,携程第一赖出现了同效仿代码兼容三端的景。这个开效率杠杠的,团队尝试到了甜头,于是乎后续的频道骨干都开始了Hybrid开发,到自离时,整个机制就挺熟了,而前者也发出几百人了。

现象重现

狼厂有三大妈流量APP,手机百度、百度地图、糯米APP,最近交接糯米的当儿,发现她们啊以做Hybrid平台化相关的放开,将静态资源打包至Native中,Native提供js调用原生应用的能力,从产品化和工程化来说做的酷对,但是发生零星单毛病:


资源总体打包至Naive中APP尺寸会增大,就算以增量机制吗避免不了APP的膨大,因为今连着的频道于少一个频段500K从来不觉得,一旦平台化后主APP尺寸会急剧增大


糯米前端框架团队包装了Native端的能力,但是并未提供配套的前端框架,这个解决方案是匪完整的。很多工作已来H5站点了,为了接通还得单独支出同效程序;而就是初业务交接,又见面面临嵌入资源要是静态资源的界定,做出来的种类没有SEO,如果关注SEO的口舌或得重出,从工程角度来说是发问题之。

而是自从成品可连入度与产品化来说,糯米Hybrid化的怪方向是那个乐天的,也真的获得了有成,在少日尽管闹成百上千频道接入了,随着推广进行,明年或会见形成一个大型的Hybrid平台。但是以自身为更过推广框架,当听到他们忽悠我说性能会增强70%,与Native体验基本一致时,不知何故我甚至笑了……

总结

倘念了点几乎只故事你依旧不清楚为何要用Hybrid技术吧,我这边再次开一个总吧:

Hybrid开发效率高、跨平台、低层本
Hybrid从业务开发上讲,没有版本问题,有BUG能及时修复

Hybrid是来缺失点的,Hybrid体验就必没有Native,所以采取有那情景,但是对于需快速试错、快速占领市场的集体来说,Hybrid一定是休次之选取,团队生活下去后抑用做经验更好之原生APP

好了,上面拉了那基本上没因此的物,今天之目的其实是为大家介绍Hybrid的部分企划学问,如果你认真看此文,可能于偏下地方针对而持有帮助:

① Hybrid中Native与前者各自的劳作是啊

② Hybrid的并行接口如何设计

③ Hybrid的Header如何设计

④ Hybrid的怎么样筹划目录结构和增量机制如何贯彻

⑤ 资源缓存策略,白屏问题……

文中是自我个人的有些开销经历,希望对各位有因此,也意在各位万般支持讨论,指出文中不足同提出您的片段建议

然后文中Andriod相关代码由本人的同事明月提供,这里特别感谢明月同窗对本人的支持,这里扫描二维码可以下载APP进行测试:

Andriod APP二维码:

图片 8

代码地址:

https://github.com/yexiaochai/hybrid

Native与前者分工

每当召开Hybrid架构设计之前用分清Native与前者的度,首先Native提供的是一宿主环境,要客观的应用Native提供的力,要落实通用的Hybrid平台架构,站于前者视角,我道待考虑以下基本设计问题。

相互设计

Hybrid架构设计第一单假设考虑的题目是怎么筹划与前者的彼此,如果这块设计之不好会对持续开发、前端框架保护造成深远的熏陶,并且这种影响数是不可逆的,所以这里需要前端和Native好好配合,提供通用的接口,比如:

① NativeUI组件,header组件、消息类组件

② 通讯录、系统、设备信息读取接口


H5与Native的竞相跳转,比如H5如何过到一个Native页面,H5如何新开Webview做动画跳到其他一个H5页面

资源访问机制

Native首先需要考虑怎样看H5资源,做到既能够坐file的方访Native内部资源,又会动用url的法访问线上资源;需要提供前端资源增量替换机制,以摆脱APP迭代发版问题,避免用户升级APP。这里虽会干到静态资源以APP中的存放策略,更新策略的规划,复杂的言语还会涉及到劳动器端的支撑。

账号信息设计

账号体系是非同小可而且无法避免的,Native需要统筹可以安全之身份验证机制,保证这块对工作开发者足够透明,打通账户信息。

Hybrid开发调试

功能设计完并无是了,Native与前者需要商谈出同模拟只是开调试之范,不然很多作业开销的行事以难以接续,这个群文章曾接受过了,本文不赘述。

关于Native还会见关切之一些通讯设计、并作设计、异常处理、日志监控和安康模块因为无是本身干的天地就是不予关注了(事实上是想念关注不得其门),而前者要开的事务就是封装Native提供的各种力量,整体架构是这样的:

图片 9

实在工作开销时,Native除了会关注登录模块之外还见面卷入支付等重点模块,这里看看工作而定。

Native与前者分工

每当召开Hybrid架构设计之前需要分清Native与前者的度,首先Native提供的是一宿主条件,要合理的使Native提供的力量,要落实通用的Hybrid平台架构,站在前端视角,我觉着待考虑以下为主设计问题。

互相设计

Hybrid架构设计第一个比方考虑的问题是哪计划和前者的相,如果这块设计之不好会对后续开发、前端框架保护造成深远的震慑,并且这种影响数是不可逆的,所以这边要前端和Native好好配合,提供通用的接口,比如:

① NativeUI组件,header组件、消息类组件

② 通讯录、系统、设备信息读取接口


H5与Native的互跳转,比如H5如何过到一个Native页面,H5如何新开Webview做动画跳到任何一个H5页面

资源访问机制

Native首先得考虑如何看H5资源,做到既会盖file的措施访Native内部资源,又能够以url的艺术访问线上资源;需要提供前端资源增量替换机制,以摆脱APP迭代发版问题,避免用户升级APP。这里就会见波及到静态资源以APP中之存策略,更新策略的设计,复杂的言语还会见提到到服务器端的支持。

账号信息设计

账号体系是重中之重而且无法避免的,Native需要规划好安全的身份验证机制,保证这块对事情开发者足够透明,打通账户信息。

Hybrid开发调试

功能设计完并无是终结,Native与前者需要商谈出同模仿只是支付调试之模子,不然很多作业支出的行事拿难以持续,这个群篇章都领了了,本文不赘述。

至于Native还会关心的一些报道设计、并作设计、异常处理、日志监控和安模块坐无是我提到的小圈子就是不予关注了(事实上是怀念关注不得其门),而前者要召开的作业虽是封装Native提供的各种能力,整体架构是这般的:

图片 10

忠实工作开销时,Native除了会关心登录模块之外还会见卷入支付等根本模块,这里看看工作要肯定。

Hybrid交互设计

Hybrid的相互无非是Native调用前端页面的JS方法,或者前端页面通过JS调用Native提供的接口,两者并行的桥梁皆Webview:

图片 11

app自身可以打定义url schema,并且将由定义的url注册在调度中心, 例如

咱们JS与Native通信一般就是是创办这好像URL被Native捕获处理,后续也应运而生了另外前端调用Native的法门,但可以举行底层封装使其透明化,所以要与是怎么进展前端和Native的并行设计。

Hybrid交互设计

Hybrid的相互无非是Native调用前端页面的JS方法,或者前端页面通过JS调用Native提供的接口,两者并行的桥梁皆Webview:

图片 12

app自身可以打定义url schema,并且将从定义的url注册在调度中心, 例如

我们JS与Native通信一般就是开创这类似URL被Native捕获处理,后续也起了另外前端调用Native的道,但可以举行底层封装使该透明化,所以最主要以及是哪开展前端和Native的并行设计。

JS to Native

Native在每个版本会提供一些API,前端会来一个相应的框架团队对那个进展打包,释放工作接口。比如糯米对外的接口是如此的:

JavaScript

BNJS.http.get();//向业务服务器将要求据【1.0】 1.3版本接口有扩大
BNJS.http.post();//向工作服务器交由数据【1.0】
BNJS.http.sign();//计算签名【1.0】
BNJS.http.getNA();//向NA服务器将要求据【1.0】 1.3本子接口有扩张
BNJS.http.postNA();//向NA服务器交由数据【1.0】
BNJS.http.getCatgData();//从Native本地得到筛选数据【1.1】

1
2
3
4
5
6
BNJS.http.get();//向业务服务器拿请求据【1.0】 1.3版本接口有扩展
BNJS.http.post();//向业务服务器提交数据【1.0】
BNJS.http.sign();//计算签名【1.0】
BNJS.http.getNA();//向NA服务器拿请求据【1.0】 1.3版本接口有扩展
BNJS.http.postNA();//向NA服务器提交数据【1.0】
BNJS.http.getCatgData();//从Native本地获取筛选数据【1.1】

JavaScript

BNJSReady(function(){ BNJS.http.post({ url :
‘http://cp01-testing-tuan02.cp01.baidu.com:8087/naserver/user/feedback’,
params : { msg : ‘测试post’, contact : ‘18721687903’ }, onSuccess :
function(res){ alert(‘发送post请求成功!’); }, onFail : function(res){
alert(‘发送post请求失败!’); } }); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
BNJSReady(function(){
    BNJS.http.post({
        url : ‘http://cp01-testing-tuan02.cp01.baidu.com:8087/naserver/user/feedback’,
        params : {
            msg : ‘测试post’,
            contact : ‘18721687903’
        },
        onSuccess : function(res){
            alert(‘发送post请求成功!’);
        },
        onFail : function(res){
            alert(‘发送post请求失败!’);
        }
    });
});

前者框架定义了一个全局变量BNJS作为Native与前者交互的靶子,只要引入了糯米提供的这个JS库,并且在糯米封装的Webview容器中,前端便拿走了调用Native的力,我想糯米这种计划是坐这样便于第三着团队的接入使用,手机百度有雷同放缓好应用框架为动的这种途径:

JavaScript

clouda.mbaas.account //释放了clouda全局变量

1
clouda.mbaas.account //释放了clouda全局变量

如此做来一个前提是,Native本身已经生平稳了,很少新增功能了,否则在直连情况下虽会面临一个狼狈,因为web站点永远保持最新的,就会见当一部分低版本容器中调用了从未供的Native能力要报错。

JS to Native

Native在每个版本会提供一些API,前端会生出一个遥相呼应之框架团队对那个进行包装,释放工作接口。比如糯米对外的接口是如此的:

图片 13

1 BNJS.http.get();//向业务服务器拿请求据【1.0】 1.3版本接口有扩展
2 BNJS.http.post();//向业务服务器提交数据【1.0】
3 BNJS.http.sign();//计算签名【1.0】
4 BNJS.http.getNA();//向NA服务器拿请求据【1.0】 1.3版本接口有扩展
5 BNJS.http.postNA();//向NA服务器提交数据【1.0】
6 BNJS.http.getCatgData();//从Native本地获取筛选数据【1.1】

图片 14

图片 15

 1 BNJSReady(function(){
 2     BNJS.http.post({
 3         url : 'http://cp01-testing-tuan02.cp01.baidu.com:8087/naserver/user/feedback',
 4         params : {
 5             msg : '测试post',
 6             contact : '18721687903'
 7         },
 8         onSuccess : function(res){
 9             alert('发送post请求成功!');
10         },
11         onFail : function(res){
12             alert('发送post请求失败!');
13         }
14     });
15 });

图片 16

前者框架定义了一个全局变量BNJS作为Native与前者交互的靶子,只要引入了糯米提供的这个JS库,并且于糯米封装的Webview容器中,前端便抱了调用Native的力,我想来糯米这种计划是坐这么方便第三着团队的连使用,手机百度有一样款好应用框架为倒的这种路径:

clouda.mbaas.account //释放了clouda全局变量

然做来一个前提是,Native本身都老安定了,很少新增功能了,否则在直连情况下便会见面临一个两难,因为web站点永远保持最新的,就见面以有低版本容器中调用了从未有过提供的Native能力而报错。

API式交互

手白、糯米底层如何做我们未能得知,但我们发现调用Native
API接口的章程跟咱们采用AJAX调用服务器端提供的接口是及其相似之:

图片 17

这里仿佛的薄开放平台的接口是这么定义的:

粉丝服务(新手接入指南)

读取接口

接到信息

接过用户私信、关注、取消关注、@等消息接口

描绘副接口

发送信息

为用户回复私信消息接口

生成带参数的次维码

生成带参数的亚维码接口

我们只要举行的即是经过同样种植办法创造ajax请求即可:

JavaScript

https://api.weibo.com/2/statuses/public\_timeline.json

1
https://api.weibo.com/2/statuses/public_timeline.json

故而我以实质上设计Hybrid交互模型时,是因接口也单位展开设计之,比如取通讯录的总体交互是:

图片 18

API式交互

手白、糯米底层如何做我们决不能得知,但咱发现调用Native
API接口的法子及我们应用AJAX调用服务器端提供的接口是连同相似的:

图片 19

这边仿佛之细小开放平台的接口是如此定义的:

粉丝服务(新手接入指南)

读取接口

接信息

吸纳用户私信、关注、取消关注、@等消息接口 

写副接口

发送信息

望用户回复私信消息接口 

生成带参数的次维码

生成带参数的亚维码接口 

咱俩只要做的即使是通过同样种植方法创造ajax请求即可:

https://api.weibo.com/2/statuses/public_timeline.json

因而自己以实质上设计Hybrid交互模型时,是盖接口也单位展开规划的,比如取通讯录的整交互是:

图片 20

格式约定

相的第一步是统筹数据格式,这里分为请求数据格式和应数据格式,参考ajax的请求模型大概是:

$.ajax(options) ⇒ XMLHttpRequest type (默认值:”GET”)
HTTP的要方法(“GET”, “POST”, or other)。 url (默认值:当前url)
请求的url地址。 data (默认值:none)
请求中寓的数,对于GET请求来说,这是含查询字符串的url地址,如果是带有的是object的讲话,$.param会将那个转化成string。

1
2
3
4
$.ajax(options) ⇒ XMLHttpRequest
type (默认值:"GET") HTTP的请求方法(“GET”, “POST”, or other)。
url (默认值:当前url) 请求的url地址。
data (默认值:none) 请求中包含的数据,对于GET请求来说,这是包含查询字符串的url地址,如果是包含的是object的话,$.param会将其转化成string。

因此我随即边和Native约定的要模型是:

JavaScript

requestHybrid({ //创建一个初的webview对话框窗口 tagname: ‘hybridapi’,
//请求参数,会为Native使用 param: {}, //Native处理成后回调前端的章程
callback: function (data) { } });

1
2
3
4
5
6
7
8
9
requestHybrid({
  //创建一个新的webview对话框窗口
  tagname: ‘hybridapi’,
  //请求参数,会被Native使用
  param: {},
  //Native处理成功后回调前端的方法
  callback: function (data) {
  }
});

本条方式执行会形成一个URL,比如:

hybridschema://hybridapi?callback=hybrid_1446276509894&param=%7B%22data1%22%3A1%2C%22data2%22%3A2%7D

这里取一点,APP安装后会以大哥大及登记一个schema,比如淘宝是taobao://,Native会有一个进程监控Webview发出的所有schema://请求,然后分发及“控制器”hybridapi处理程序,Native控制器处理时见面得param提供的参数(encode过),处理终结晚用携数量获得Webview
window对象中的callback(hybrid_1446276509894)调用之

数返回的格式约定是:

JavaScript

{ data: {}, errno: 0, msg: “success” }

1
2
3
4
5
{
  data: {},
  errno: 0,
  msg: "success"
}

实的多寡在data对象被,如果errno不为0的口舌,便需提醒msg,这里选出个例证如果不当码1象征该接口需要升级app才能够采取的言辞:

JavaScript

{ data: {}, errno: 1, msg: “APP版本过低,请升级APP版本” }

1
2
3
4
5
{
  data: {},
  errno: 1,
  msg: "APP版本过低,请升级APP版本"
}

代码实现

此间为一个简易的代码实现,真实代码在APP中会有着转:

JavaScript

window.Hybrid = window.Hybrid || {}; var bridgePostMsg = function (url)
{ if ($.os.ios) { window.location = url; } else { var ifr =
$(‘<iframe style=”display: none;” src=”‘ + url + ‘”/>’);
$(‘body’).append(ifr); setTimeout(function () { ifr.remove(); }, 1000) }
}; var _getHybridUrl = function (params) { var k, paramStr = ”, url =
‘scheme://’; url += params.tagname + ‘?t=’ + new Date().getTime();
//时间戳,防止url不起效 if (params.callback) { url += ‘&callback=’ +
params.callback; delete params.callback; } if (params.param) { paramStr
= typeof params.param == ‘object’ ? JSON.stringify(params.param) :
params.param; url += ‘&param=’ + encodeURIComponent(paramStr); } return
url; }; var requestHybrid = function (params) {
//生成唯一实行函数,执行后绝迹 var tt = (new Date().getTime()); var t =
‘hybrid_’ + tt; var tmpFn; //处理有回调的景况 if (params.callback) {
tmpFn = params.callback; params.callback = t; window.Hybrid[t] =
function (data) { tmpFn(data); delete window.Hybrid[t]; } }
bridgePostMsg(_getHybridUrl(params)); };
//获取版本信息,约定APP的navigator.userAgent版本包含版本信息:scheme/xx.xx.xx
var getHybridInfo = function () { var platform_version = {}; var na =
navigator.userAgent; var info = na.match(/scheme\/\d\.\d\.\d/); if
(info && info[0]) { info = info[0].split(‘/’); if (info &&
info.length == 2) { platform_version.platform = info[0];
platform_version.version = info[1]; } } return platform_version; };

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
window.Hybrid = window.Hybrid || {};
var bridgePostMsg = function (url) {
    if ($.os.ios) {
        window.location = url;
    } else {
        var ifr = $(‘<iframe style="display: none;" src="’ + url + ‘"/>’);
        $(‘body’).append(ifr);
        setTimeout(function () {
            ifr.remove();
        }, 1000)
    }
};
var _getHybridUrl = function (params) {
    var k, paramStr = ”, url = ‘scheme://’;
    url += params.tagname + ‘?t=’ + new Date().getTime(); //时间戳,防止url不起效
    if (params.callback) {
        url += ‘&callback=’ + params.callback;
        delete params.callback;
    }
    if (params.param) {
        paramStr = typeof params.param == ‘object’ ? JSON.stringify(params.param) : params.param;
        url += ‘&param=’ + encodeURIComponent(paramStr);
    }
    return url;
};
var requestHybrid = function (params) {
    //生成唯一执行函数,执行后销毁
    var tt = (new Date().getTime());
    var t = ‘hybrid_’ + tt;
    var tmpFn;
 
    //处理有回调的情况
    if (params.callback) {
        tmpFn = params.callback;
        params.callback = t;
        window.Hybrid[t] = function (data) {
            tmpFn(data);
            delete window.Hybrid[t];
        }
    }
    bridgePostMsg(_getHybridUrl(params));
};
//获取版本信息,约定APP的navigator.userAgent版本包含版本信息:scheme/xx.xx.xx
var getHybridInfo = function () {
    var platform_version = {};
    var na = navigator.userAgent;
    var info = na.match(/scheme\/\d\.\d\.\d/);
 
    if (info && info[0]) {
        info = info[0].split(‘/’);
        if (info && info.length == 2) {
            platform_version.platform = info[0];
            platform_version.version = info[1];
        }
    }
    return platform_version;
};

因Native对于H5来是脚,框架&底层一般的话是无见面关心业务实现的,所以真工作中Native调用H5场景较少,这里不予关注了。

格式约定

互动的率先步是计划数据格式,这里分为请求数据格式和响应数据格式,参考ajax的呼吁模型大概是:

$.ajax(options) ⇒ XMLHttpRequest
type (默认值:"GET") HTTP的请求方法(“GET”, “POST”, or other)。
url (默认值:当前url) 请求的url地址。
data (默认值:none) 请求中包含的数据,对于GET请求来说,这是包含查询字符串的url地址,如果是包含的是object的话,$.param会将其转化成string。

之所以自己就边与Native约定的请求模型是:

图片 21

requestHybrid({
  //创建一个新的webview对话框窗口
  tagname: 'hybridapi',
  //请求参数,会被Native使用
  param: {},
  //Native处理成功后回调前端的方法
  callback: function (data) {
  }
});

图片 22

斯主意执行会形成一个URL,比如:

hybridschema://hybridapi?callback=hybrid_1446276509894&param=%7B%22data1%22%3A1%2C%22data2%22%3A2%7D

此地取一点,APP安装后会以大哥大及注册一个schema,比如淘宝是taobao://,Native会有一个过程监控Webview发出的拥有schema://请求,然后分发到“控制器”hybridapi处理程序,Native控制器处理常会见需要param提供的参数(encode过),处理完毕后用携带数量获得Webview
window对象吃之callback(hybrid_1446276509894)调用之

多少返回的格式约定是:

{
  data: {},
  errno: 0,
  msg: "success"
}

诚的数以data对象被,如果errno不为0的言辞,便要提醒msg,这里选出个例如果不当码1意味着该接口需要升级app才会使用的话语:

{
  data: {},
  errno: 1,
  msg: "APP版本过低,请升级APP版本"
}

代码实现

这边叫一个大概的代码实现,真实代码在APP中见面有着变更:

图片 23

 1 window.Hybrid = window.Hybrid || {};
 2 var bridgePostMsg = function (url) {
 3     if ($.os.ios) {
 4         window.location = url;
 5     } else {
 6         var ifr = $('<iframe style="display: none;" src="' + url + '"/>');
 7         $('body').append(ifr);
 8         setTimeout(function () {
 9             ifr.remove();
10         }, 1000)
11     }
12 };
13 var _getHybridUrl = function (params) {
14     var k, paramStr = '', url = 'scheme://';
15     url += params.tagname + '?t=' + new Date().getTime(); //时间戳,防止url不起效
16     if (params.callback) {
17         url += '&callback=' + params.callback;
18         delete params.callback;
19     }
20     if (params.param) {
21         paramStr = typeof params.param == 'object' ? JSON.stringify(params.param) : params.param;
22         url += '&param=' + encodeURIComponent(paramStr);
23     }
24     return url;
25 };
26 var requestHybrid = function (params) {
27     //生成唯一执行函数,执行后销毁
28     var tt = (new Date().getTime());
29     var t = 'hybrid_' + tt;
30     var tmpFn;
31 
32     //处理有回调的情况
33     if (params.callback) {
34         tmpFn = params.callback;
35         params.callback = t;
36         window.Hybrid[t] = function (data) {
37             tmpFn(data);
38             delete window.Hybrid[t];
39         }
40     }
41     bridgePostMsg(_getHybridUrl(params));
42 };
43 //获取版本信息,约定APP的navigator.userAgent版本包含版本信息:scheme/xx.xx.xx
44 var getHybridInfo = function () {
45     var platform_version = {};
46     var na = navigator.userAgent;
47     var info = na.match(/scheme\/\d\.\d\.\d/);
48 
49     if (info && info[0]) {
50         info = info[0].split('/');
51         if (info && info.length == 2) {
52             platform_version.platform = info[0];
53             platform_version.version = info[1];
54         }
55     }
56     return platform_version;
57 };

图片 24

坐Native对于H5来是底层,框架&底层一般的话是免见面关心工作实现之,所以实际工作中Native调用H5场景较少,这里不予关注了。

常用交互API

大好的竞相设计是打响之第一步,在实事求是工作支付被发生部分API一定会就此到。

常用交互API

精彩的相设计是打响之首先步,在真正工作支出被生一对API一定会就此到。

跳转

跳转是Hybrid必用API之一,对前者来说出以下跳转:

① 页面内跳转,与Hybrid无关

② H5跳转Native界面

③ H5初开Webview跳转H5页面,一般为举行页面动画切换

假设假定用动画片,按工作以来出上同往后少栽,forward&back,所以约定如下,首先是H5跳Native某一个页面

JavaScript

//H5跳Native页面
//=>baidubus://forward?t=1446297487682&param=%7B%22topage%22%3A%22home%22%2C%22type%22%3A%22h2n%22%2C%22data2%22%3A2%7D
requestHybrid({ tagname: ‘forward’, param: { //要去到的页面 topage:
‘home’, //跳转方式,H5跳Native type: ‘native’, //其它参数 data2: 2 } });

1
2
3
4
5
6
7
8
9
10
11
12
13
//H5跳Native页面
//=>baidubus://forward?t=1446297487682&param=%7B%22topage%22%3A%22home%22%2C%22type%22%3A%22h2n%22%2C%22data2%22%3A2%7D
requestHybrid({
    tagname: ‘forward’,
    param: {
        //要去到的页面
        topage: ‘home’,
        //跳转方式,H5跳Native
        type: ‘native’,
        //其它参数
        data2: 2
    }
});

按携程H5页面要错过到酒吧Native某一个页面可以这样:

JavaScript

//=>schema://forward?t=1446297653344&param=%7B%22topage%22%3A%22hotel%2Fdetail%20%20%22%2C%22type%22%3A%22h2n%22%2C%22id%22%3A20151031%7D
requestHybrid({ tagname: ‘forward’, param: { //要去交之页面 topage:
‘hotel/detail’, //跳转方式,H5跳Native type: ‘native’, //其它参数 id:
20151031 } });

1
2
3
4
5
6
7
8
9
10
11
12
//=>schema://forward?t=1446297653344&param=%7B%22topage%22%3A%22hotel%2Fdetail%20%20%22%2C%22type%22%3A%22h2n%22%2C%22id%22%3A20151031%7D
requestHybrid({
    tagname: ‘forward’,
    param: {
        //要去到的页面
        topage: ‘hotel/detail’,
        //跳转方式,H5跳Native
        type: ‘native’,
        //其它参数
        id: 20151031
    }
});

遵循H5新开Webview的道跳转H5页面就可以这么:

JavaScript

requestHybrid({ tagname: ‘forward’, param: {
//要去到的页面,首先找到hotel频道,然后定位到detail模块 topage:
‘hotel/detail ‘, //跳转方式,H5新开Webview跳转,最后装载H5页面 type:
‘webview’, //其它参数 id: 20151031 } });

1
2
3
4
5
6
7
8
9
10
11
requestHybrid({
    tagname: ‘forward’,
    param: {
        //要去到的页面,首先找到hotel频道,然后定位到detail模块
        topage: ‘hotel/detail  ‘,
        //跳转方式,H5新开Webview跳转,最后装载H5页面
        type: ‘webview’,
        //其它参数
        id: 20151031
    }
});

back与forward一致,我们竟然会起animattype参数决定切换页面时的动画效果,真实使用时或者会见卷入全局方法有些去tagname的细节,这时便同糯米对外放出的接口差不多了。

跳转

跳转是Hybrid必用API之一,对前者来说有以下跳转:

① 页面内跳转,与Hybrid无关

② H5跳转Native界面

③ H5新开Webview跳转H5页面,一般也做页面动画切换

如若只要运动画片,按工作以来有进同于后少种,forward&back,所以约定如下,首先是H5跳Native某一个页面

图片 25

 1 //H5跳Native页面
 2 //=>baidubus://forward?t=1446297487682&param=%7B%22topage%22%3A%22home%22%2C%22type%22%3A%22h2n%22%2C%22data2%22%3A2%7D
 3 requestHybrid({
 4     tagname: 'forward',
 5     param: {
 6         //要去到的页面
 7         topage: 'home',
 8         //跳转方式,H5跳Native
 9         type: 'native',
10         //其它参数
11         data2: 2
12     }
13 });

图片 26

比如携程H5页面要去到酒楼Native某一个页面可以如此:

图片 27

 1 //=>schema://forward?t=1446297653344&param=%7B%22topage%22%3A%22hotel%2Fdetail%20%20%22%2C%22type%22%3A%22h2n%22%2C%22id%22%3A20151031%7D
 2 requestHybrid({
 3     tagname: 'forward',
 4     param: {
 5         //要去到的页面
 6         topage: 'hotel/detail',
 7         //跳转方式,H5跳Native
 8         type: 'native',
 9         //其它参数
10         id: 20151031
11     }
12 });

图片 28

随H5新开Webview的道跳转H5页面就可以这么:

图片 29

 1 requestHybrid({
 2     tagname: 'forward',
 3     param: {
 4         //要去到的页面,首先找到hotel频道,然后定位到detail模块
 5         topage: 'hotel/detail  ',
 6         //跳转方式,H5新开Webview跳转,最后装载H5页面
 7         type: 'webview',
 8         //其它参数
 9         id: 20151031
10     }
11 });

图片 30

back与forward一致,我们甚至会见有animattype参数决定切换页面时之卡通效果,真实使用时或许会见卷入全局方法有些去tagname的底细,这时便和糯米对外释放的接口差不多了。

Header 组件的设计

最初自己其实是对抗下Native提供的UI组件的,尤其是Header,因为平台化后,Native每次变更都坏慎重并且响应很缓慢,但是由两碰基本要素考虑,我为主放弃了抵抗:

① 其它主流容器都是这般做的,比如微信、手机百度、携程


没有header一旦网络出错出现白屏,APP将深陷假死状态,这是不可接受的,而貌似的化解方案都极端事务了

PS:Native吊起Native时,如果300ms没有响应需要来loading组件,避免白屏

因为H5站点本来就是有Header组件,站于前者框架层来说,需要保证工作的代码是均等的,所有的差距需要在框架层就透明化,简单的话Header的筹划得按:

① H5 header组件与Native提供的header组件使用调用层接口一致

② 前端框架层根据环境判断选择相应用H5的header组件抑或Native的header组件

相似的话header组件需要完成以下职能:


header左侧与右手可安排,显示为亲笔或图标(这里要求header实现主流图标,并且也可是由于工作控制图标),并需要控制该点击回调


header的title可安装也就标题或者主标题、子标题类型,并且只是配备lefticon与righticon(icon居中)

③ 满足一些特配备,比如标签类header

于是,站在前端业务方来说,header的行使办法呢(其中tagname是不允许再的):

JavaScript

//Native以及前端框架会指向新鲜tagname的标识做默认回调,如果非报callback,或者点击回调callback无返回则履行默认方法
//
back前端默认执行History.back,如果不行后降则回指定URL,Native如果检测到不可后回落则赶回Naive大首页
// home前端默认返回指定URL,Native默认返回大首页 this.header.set({ left:
[ { //如果出现value字段,则默认不行使icon tagname: ‘back’, value:
‘回退’, //如果设置了lefticon或者righticon,则显示icon
//native会提供常用图标icon映射,如果找不顶,便会去时业务频道专用目录获取图标
lefticon: ‘back’, callback: function () { } } ], right: [ {
//默认icon为tagname,这里也icon tagname: ‘search’, callback: function ()
{ } }, //自定义图标 { tagname: ‘me’,
//会去hotel频道存储静态header图标资源目录搜寻该图标,没有就采取默认图标
icon: ‘hotel/me.png’, callback: function () { } } ], title: ‘title’,
//显示主标题,子标题的景象 title: [‘title’, ‘subtitle’], //定制化title
title: { value: ‘title’, //标题右边图标 righticon: ‘down’,
//也可以安装lefticon //标题类型,默认为空,设置的口舌需要特别处理 //type:
‘tabs’, //点击标题时的回调,默认为空 callback: function () { } } });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//Native以及前端框架会对特殊tagname的标识做默认回调,如果未注册callback,或者点击回调callback无返回则执行默认方法
// back前端默认执行History.back,如果不可后退则回到指定URL,Native如果检测到不可后退则返回Naive大首页
// home前端默认返回指定URL,Native默认返回大首页
this.header.set({
    left: [
        {
            //如果出现value字段,则默认不使用icon
            tagname: ‘back’,
            value: ‘回退’,
            //如果设置了lefticon或者righticon,则显示icon
            //native会提供常用图标icon映射,如果找不到,便会去当前业务频道专用目录获取图标
            lefticon: ‘back’,
            callback: function () { }
        }
    ],
    right: [
        {
            //默认icon为tagname,这里为icon
            tagname: ‘search’,
            callback: function () { }
        },
    //自定义图标
        {
        tagname: ‘me’,
        //会去hotel频道存储静态header图标资源目录搜寻该图标,没有便使用默认图标
        icon: ‘hotel/me.png’,
        callback: function () { }
    }
    ],
    title: ‘title’,
    //显示主标题,子标题的场景
    title: [‘title’, ‘subtitle’],
 
    //定制化title
    title: {
        value: ‘title’,
        //标题右边图标
        righticon: ‘down’, //也可以设置lefticon
        //标题类型,默认为空,设置的话需要特殊处理
        //type: ‘tabs’,
        //点击标题时的回调,默认为空
        callback: function () { }
    }
});

盖Header左边一般的话只是发一个按钮,所以那个目标可以使用这种形式:

JavaScript

this.header.set({ back: function () { }, title: ” }); //语法糖=>
this.header.set({ left: [{ tagname: ‘back’, callback: function(){} }],
title: ”, });

1
2
3
4
5
6
7
8
9
10
11
12
this.header.set({
    back: function () { },
    title: ”
});
//语法糖=>
this.header.set({
    left: [{
        tagname: ‘back’,
        callback: function(){}
    }],
    title: ”,
});

否就Native端的贯彻,这里见面新添鲜个接口,向Native注册事件,以及取消事件:

JavaScript

var registerHybridCallback = function (ns, name, callback) {
if(!window.Hybrid[ns]) window.Hybrid[ns] = {};
window.Hybrid[ns][name] = callback; }; var unRegisterHybridCallback
= function (ns) { if(!window.Hybrid[ns]) return; delete
window.Hybrid[ns]; };

1
2
3
4
5
6
7
8
9
var registerHybridCallback = function (ns, name, callback) {
  if(!window.Hybrid[ns]) window.Hybrid[ns] = {};
  window.Hybrid[ns][name] = callback;
};
 
var unRegisterHybridCallback = function (ns) {
  if(!window.Hybrid[ns]) return;
  delete window.Hybrid[ns];
};

Native Header组件的兑现:

JavaScript

define([], function () { ‘use strict’; return _.inherit({ propertys:
function () { this.left = []; this.right = []; this.title = {};
this.view = null; this.hybridEventFlag = ‘Header_Event’; }, //全部翻新
set: function (opts) { if (!opts) return; var left = []; var right =
[]; var title = {}; var tmp = {}; //语法糖适配 if (opts.back) { tmp =
{ tagname: ‘back’ }; if (typeof opts.back == ‘string’) tmp.value =
opts.back; else if (typeof opts.back == ‘function’) tmp.callback =
opts.back; else if (typeof opts.back == ‘object’) _.extend(tmp,
opts.back); left.push(tmp); } else { if (opts.left) left = opts.left; }
//右边按钮必须保持数据一致性 if (typeof opts.right == ‘object’ &&
opts.right.length) right = opts.right if (typeof opts.title == ‘string’)
{ title.title = opts.title; } else if (_.isArray(opts.title) &&
opts.title.length > 1) { title.title = opts.title[0];
title.subtitle = opts.title[1]; } else if (typeof opts.title ==
‘object’) { _.extend(title, opts.title); } this.left = left; this.right
= right; this.title = title; this.view = opts.view;
this.registerEvents(); _.requestHybrid({ tagname: ‘updateheader’,
param: { left: this.left, right: this.right, title: this.title } }); },
//注册事件,将事件存于本地 registerEvents: function () {
_.unRegisterHybridCallback(this.hybridEventFlag);
this._addEvent(this.left); this._addEvent(this.right);
this._addEvent(this.title); }, _addEvent: function (data) { if
(!_.isArray(data)) data = [data]; var i, len, tmp, fn, tagname; var t
= ‘header_’ + (new Date().getTime()); for (i = 0, len = data.length; i
< len; i++) { tmp = data[i]; tagname = tmp.tagname || ”; if
(tmp.callback) { fn = $.proxy(tmp.callback, this.view); tmp.callback =
t; _.registerHeaderCallback(this.hybridEventFlag, t + ‘_’ + tagname,
fn); } } }, //显示header show: function () { _.requestHybrid({ tagname:
‘showheader’ }); }, //隐藏header hide: function () { _.requestHybrid({
tagname: ‘hideheader’, param: { animate: true } }); },
//只更新title,不重置事件,不对准header其它地方导致变化,仅仅不过简易的header能如此操作
update: function (title) { _.requestHybrid({ tagname:
‘updateheadertitle’, param: { title: ‘aaaaa’ } }); }, initialize:
function () { this.propertys(); } }); }); Native Header组件的包装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
define([], function () {
    ‘use strict’;
 
    return _.inherit({
 
        propertys: function () {
 
            this.left = [];
            this.right = [];
            this.title = {};
            this.view = null;
 
            this.hybridEventFlag = ‘Header_Event’;
 
        },
 
        //全部更新
        set: function (opts) {
            if (!opts) return;
 
            var left = [];
            var right = [];
            var title = {};
            var tmp = {};
 
            //语法糖适配
            if (opts.back) {
                tmp = { tagname: ‘back’ };
                if (typeof opts.back == ‘string’) tmp.value = opts.back;
                else if (typeof opts.back == ‘function’) tmp.callback = opts.back;
                else if (typeof opts.back == ‘object’) _.extend(tmp, opts.back);
                left.push(tmp);
            } else {
                if (opts.left) left = opts.left;
            }
 
            //右边按钮必须保持数据一致性
            if (typeof opts.right == ‘object’ && opts.right.length) right = opts.right
 
            if (typeof opts.title == ‘string’) {
                title.title = opts.title;
            } else if (_.isArray(opts.title) && opts.title.length > 1) {
                title.title = opts.title[0];
                title.subtitle = opts.title[1];
            } else if (typeof opts.title == ‘object’) {
                _.extend(title, opts.title);
            }
 
            this.left = left;
            this.right = right;
            this.title = title;
            this.view = opts.view;
 
            this.registerEvents();
 
            _.requestHybrid({
                tagname: ‘updateheader’,
                param: {
                    left: this.left,
                    right: this.right,
                    title: this.title
                }
            });
 
        },
 
        //注册事件,将事件存于本地
        registerEvents: function () {
            _.unRegisterHybridCallback(this.hybridEventFlag);
            this._addEvent(this.left);
            this._addEvent(this.right);
            this._addEvent(this.title);
        },
 
        _addEvent: function (data) {
            if (!_.isArray(data)) data = [data];
            var i, len, tmp, fn, tagname;
            var t = ‘header_’ + (new Date().getTime());
 
            for (i = 0, len = data.length; i < len; i++) {
                tmp = data[i];
                tagname = tmp.tagname || ”;
                if (tmp.callback) {
                    fn = $.proxy(tmp.callback, this.view);
                    tmp.callback = t;
                    _.registerHeaderCallback(this.hybridEventFlag, t + ‘_’ + tagname, fn);
                }
            }
        },
 
        //显示header
        show: function () {
            _.requestHybrid({
                tagname: ‘showheader’
            });
        },
 
        //隐藏header
        hide: function () {
            _.requestHybrid({
                tagname: ‘hideheader’,
                param: {
                    animate: true
                }
            });
        },
 
        //只更新title,不重置事件,不对header其它地方造成变化,仅仅最简单的header能如此操作
        update: function (title) {
            _.requestHybrid({
                tagname: ‘updateheadertitle’,
                param: {
                    title: ‘aaaaa’
                }
            });
        },
 
        initialize: function () {
            this.propertys();
        }
    });
 
});
 
Native Header组件的封装

Header 组件的宏图

初期我实在是对抗下Native提供的UI组件的,尤其是Header,因为平台化后,Native每次转都坏慎重并且响应很缓慢,但是由两沾基本元素考虑,我为主放弃了抵抗:

① 其它主流容器都是这样做的,比如微信、手机百度、携程


没有header一旦网络出错出现白屏,APP将陷入假死状态,这是不可承受之,而貌似的化解方案还不过事务了

PS:Native吊起Native时,如果300ms没有响应需要来loading组件,避免白屏

因为H5站点本来就有Header组件,站于前者框架层来说,需要保证工作的代码是同等的,所有的距离需要在框架层就透明化,简单来说Header的规划得按:

① H5 header组件跟Native提供的header组件使用调用层接口一致

② 前端框架层根据条件判断选择相应用H5的header组件抑或Native的header组件

貌似的话header组件需要完成以下职能:


header左侧与右手可部署,显示为亲笔或图标(这里要求header实现主流图标,并且为不过由于工作控制图标),并需控制该点击回调


header的title可设置也就标题或者主标题、子标题类型,并且只是配备lefticon与righticon(icon居中)

③ 满足一些非同寻常配备,比如标签类header

所以,站在前端业务方来说,header的应用办法呢(其中tagname是无允许再的):

图片 31

 1 //Native以及前端框架会对特殊tagname的标识做默认回调,如果未注册callback,或者点击回调callback无返回则执行默认方法
 2 // back前端默认执行History.back,如果不可后退则回到指定URL,Native如果检测到不可后退则返回Naive大首页
 3 // home前端默认返回指定URL,Native默认返回大首页
 4 this.header.set({
 5     left: [
 6         {
 7             //如果出现value字段,则默认不使用icon
 8             tagname: 'back',
 9             value: '回退',
10             //如果设置了lefticon或者righticon,则显示icon
11             //native会提供常用图标icon映射,如果找不到,便会去当前业务频道专用目录获取图标
12             lefticon: 'back',
13             callback: function () { }
14         }
15     ],
16     right: [
17         {
18             //默认icon为tagname,这里为icon
19             tagname: 'search',
20             callback: function () { }
21         },
22     //自定义图标
23         {
24         tagname: 'me',
25         //会去hotel频道存储静态header图标资源目录搜寻该图标,没有便使用默认图标
26         icon: 'hotel/me.png',
27         callback: function () { }
28     }
29     ],
30     title: 'title',
31     //显示主标题,子标题的场景
32     title: ['title', 'subtitle'],
33 
34     //定制化title
35     title: {
36         value: 'title',
37         //标题右边图标
38         righticon: 'down', //也可以设置lefticon
39         //标题类型,默认为空,设置的话需要特殊处理
40         //type: 'tabs',
41         //点击标题时的回调,默认为空
42         callback: function () { }
43     }
44 });

图片 32

以Header左边一般的话只有来一个按钮,所以该目标好采取这种形式:

图片 33

 1 this.header.set({
 2     back: function () { },
 3     title: ''
 4 });
 5 //语法糖=>
 6 this.header.set({
 7     left: [{
 8         tagname: 'back',
 9         callback: function(){}
10     }],
11     title: '',
12 });

图片 34

也成功Native端的兑现,这里会见新加少独接口,向Native注册事件,以及取消事件:

图片 35

1 var registerHybridCallback = function (ns, name, callback) {
2   if(!window.Hybrid[ns]) window.Hybrid[ns] = {};
3   window.Hybrid[ns][name] = callback;
4 };
5 
6 var unRegisterHybridCallback = function (ns) {
7   if(!window.Hybrid[ns]) return;
8   delete window.Hybrid[ns];
9 };

图片 36

Native Header组件的实现:

图片 37 Native Header组件的包

请求类

虽说get类请求可以就此jsonp的办法绕了跨域问题,但是post请求却是确实的绊脚石,为了安全性服务器设置cors会仅仅对几个域名,Hybrid内嵌静态资源是经过file的章程读取,这种情景下cors就不好而了,所以每个请求需要经Native做同样交汇代理发出去。

图片 38

这利用状况以及Header组件一致,前端框架层必须得对业务透明化,业务实际上不必关心这请是由浏览器发出还是出于Native发出:

JavaScript

HybridGet = function (url, param, callback) { }; HybridPost = function
(url, param, callback) { };

1
2
3
4
HybridGet = function (url, param, callback) {
};
HybridPost = function (url, param, callback) {
};

真的工作场景,会将之封装及数量请求模块,在脚做适配,在H5站点下使用ajax请求,在Native内嵌时使用代理发出,与Native的预约为:

JavaScript

requestHybrid({ tagname: ‘ajax’, param: { url: ‘hotel/detail’, param:
{}, //默认为get type: ‘post’ }, //响应后底回调 callback: function (data)
{ } });

1
2
3
4
5
6
7
8
9
10
11
requestHybrid({
    tagname: ‘ajax’,
    param: {
        url: ‘hotel/detail’,
        param: {},
        //默认为get
        type: ‘post’
    },
    //响应后的回调
    callback: function (data) { }
});

请求类

尽管如此get类请求可以就此jsonp的方式绕了跨域问题,但是post请求却是确实的阻碍,为了安全性服务器设置cors会仅仅针对几独域名,Hybrid内嵌静态资源是透过file的主意读取,这种情景下cors就坏使了,所以每个请求需要通过Native做同样叠代理发出去。

图片 39

此用状况和Header组件一致,前端框架层必须做到对业务透明化,业务实际上不必关心这请是出于浏览器发出还是出于Native发出:

1 HybridGet = function (url, param, callback) {
2 };
3 HybridPost = function (url, param, callback) {
4 };

诚实的工作场景,会拿之封装及多少要模块,在底部做适配,在H5站点下使用ajax请求,在Native内嵌时使用代理发出,与Native的约定啊:

图片 40

 1 requestHybrid({
 2     tagname: 'ajax',
 3     param: {
 4         url: 'hotel/detail',
 5         param: {},
 6         //默认为get
 7         type: 'post'
 8     },
 9     //响应后的回调
10     callback: function (data) { }
11 });

图片 41

常用NativeUI组件

末段,Native会提供几乎单常因此底Native级别之UI,比如loading加载层,比如toast消息框:

JavaScript

var HybridUI = {}; HybridUI.showLoading(); //=> requestHybrid({
tagname: ‘showLoading’ }); HybridUI.showToast({ title: ‘111’,
//几秒后自动关闭提示框,-1用点击才见面倒闭 hidesec: 3,
//弹出层关闭时之回调 callback: function () { } }); //=>
requestHybrid({ tagname: ‘showToast’, param: { title: ‘111’, hidesec: 3,
callback: function () { } } });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var HybridUI = {};
HybridUI.showLoading();
//=>
requestHybrid({
    tagname: ‘showLoading’
});
 
HybridUI.showToast({
    title: ‘111’,
    //几秒后自动关闭提示框,-1需要点击才会关闭
    hidesec: 3,
    //弹出层关闭时的回调
    callback: function () { }
});
//=>
requestHybrid({
    tagname: ‘showToast’,
    param: {
        title: ‘111’,
        hidesec: 3,
        callback: function () { }
    }
});

Native
UI以及前端UI不便于打通,所以当实事求是工作开销进程遭到,一般就见面使几单举足轻重之Native
UI。

常用NativeUI组件

末,Native会提供几乎独常因此的Native级别之UI,比如loading加载层,比如toast消息框:

图片 42

 1 var HybridUI = {};
 2 HybridUI.showLoading();
 3 //=>
 4 requestHybrid({
 5     tagname: 'showLoading'
 6 });
 7 
 8 HybridUI.showToast({
 9     title: '111',
10     //几秒后自动关闭提示框,-1需要点击才会关闭
11     hidesec: 3,
12     //弹出层关闭时的回调
13     callback: function () { }
14 });
15 //=>
16 requestHybrid({
17     tagname: 'showToast',
18     param: {
19         title: '111',
20         hidesec: 3,
21         callback: function () { }
22     }
23 });

图片 43

Native
UI同前端UI不爱打通,所以在真实工作支出进程中,一般就会用几单重点的Native
UI。

账号体系的统筹

冲上面的宏图,我们约定以Hybrid中呼吁有个别种植起方式:

① 如果是webview访问线上站点的话,直接下传统ajax发出

② 如果是file的花样读取Native本地资源的言语,请求由Native代理发出

因静态html资源没有鉴权的问题,真正的权柄验证需要请服务器api响应通过错误码才能够得到,这是动态语言及静态语言做输入页面的一个生充分的区别。

坐网页的主意访,账号登录以及否由是否含有秘钥cookie决定(这时并无能够担保秘钥的行之有效),因为Native不关注业务实现,而每次载入都发或是登录成功跨越回来的结果,所以每次载入后还需关爱秘钥cookie变化,以形成登录态数据一致性。

以file的方法访内嵌资源的语,因为API请求控制方为Native,所以鉴权的干活全盘是因为Native完成,接口访问使没登录便弹出Native级别登录框引导登录即可,每次看webview将账号信息种入到webview中,这里发出个矛盾点是Native种入webview的时,因为有或是网页注销的状态,所以这边的逻辑是:

① webview载入结束

② Native检测webview是否带有账号cookie信息


如果未带有则种入cookie,如果含有则检测与Native账号信息是否相同,不同则替换自身

④ 如果检测及过到了取消账户的页面,则需要清理自家账号信息

假设登录不统一会就会起上述复杂的逻辑,所以真实情况下我们见面针对登录接口收口。

简单化账号接口

平台层面看上述操作过于复杂,便挟持要求以Hybrid容器中不得不利用Native接口进行登录和刊登,前端框架在底部做适配,保证上层业务的晶莹,这样情况会简单过剩:

① 使用Native代理做要接口,如果无登录直接Native层唤起登录框


直连道使ajax请求接口,如果没有登录则当底层唤起登录框(需要前端框架支持)

简言之的记名上出接口实现:

JavaScript

/* 无论成功与否皆会关闭登录框 参数包括: success 登录成功的回调 error
登录失败的回调 url
如果没安装success,或者success执行后尚未回true,则默认跳向这url */
HybridUI.Login = function (opts) { }; //=> requestHybrid({ tagname:
‘login’, param: { success: function () { }, error: function () { }, url:
‘…’ } }); //与登录接口一致,参数一致 HybridUI.logout = function () {
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
无论成功与否皆会关闭登录框
参数包括:
success 登录成功的回调
error 登录失败的回调
url 如果没有设置success,或者success执行后没有返回true,则默认跳往此url
*/
HybridUI.Login = function (opts) {
};
//=>
requestHybrid({
    tagname: ‘login’,
    param: {
        success: function () { },
        error: function () { },
        url: ‘…’
    }
});
//与登录接口一致,参数一致
HybridUI.logout = function () {
};

账号信息得到

每当实质上的事务支付被,判断用户是否登录、获取用户基本信息之急需比比皆是,所以这里要保证Hybrid开发模式与H5开发模式保持统一,否则用在业务代码中做过多无谓的判断,我们以前端框架会卷入一个User模块,主要接口包括:

JavaScript

1 var User = {}; 2 User.isLogin = function () { }; 3 User.getInfo =
function () { };

1
2
3
1 var User = {};
2 User.isLogin = function () { };
3 User.getInfo = function () { };

其一代码的最底层实现分为前端实现,Native实现,首先是前者的做法是:

手上端页面载入后,会开一样次异步请求,请求用户相关数据,如果是登录状态就能获取数据存于localstorage中,这里早晚不可知存取敏感信息

前者采用localstorage的言语需要考虑极端情况下用内存变量的法子替换localstorage的落实,否则会并发不可采用的景况,而持续之访皆是应用localstorage中的多寡做判定依据,以下状况用清理localstorage的账号数据:

① 系统上

② 访问接口提示用报到

③ 调用登录接口

这种模式多用于单页应用,非单页应用一般会以历次刷新页面先清空账号信息再异步拉取,但是若手上页面就就是需判定用户登录数来说,便不可因了;处于Hybrid容器中常,因为Native本身就是封存了用户信息,封装的接口直接由Native获取即可,这块比较靠谱。

账号体系的统筹

冲上面的宏图,我们约定以Hybrid中呼吁有三三两两种植起方式:

① 如果是webview访问线上站点的话,直接下传统ajax发出

② 如果是file的款式读取Native本地资源的口舌,请求由Native代理发出

为静态html资源没有鉴权的问题,真正的权柄验证需要请服务器api响应通过错误码才能够博取,这是动态语言及静态语言做输入页面的一个异常酷之区别。

坐网页的计访,账号登录以及否由是否含有秘钥cookie决定(这时并无能够保证秘钥的行之有效),因为Native不关注业务实现,而每次载入都发或是登录成功跨越回来的结果,所以每次载入后还需关注秘钥cookie变化,以形成登录态数据一致性。

以file的章程访内嵌资源的语句,因为API请求控制方为Native,所以鉴权的干活了是因为Native完成,接口访问使没登录便弹出Native级别登录框引导登录即可,每次看webview将账号信息种入到webview中,这里有个矛盾点是Native种入webview的会,因为来或是网页注销的图景,所以这边的逻辑是:

① webview载入结束

② Native检测webview是否带有账号cookie信息


如果非含有则种入cookie,如果带有则检测与Native账号信息是否一致,不同则替换自身

④ 如果检测及跨到了取消账户的页面,则要清理自家账号信息

假若登录不统一会便会现出上述复杂的逻辑,所以真实情况下我们会对登录接口收口。

简单化账号接口

阳台层面看上述操作过于复杂,便挟持要求以Hybrid容器中不得不使用Native接口进行登录和刊登,前端框架在底部做适配,保证上层业务的透明,这样状况会略多:

① 使用Native代理做要接口,如果无登录直接Native层唤起登录框


直连道使ajax请求接口,如果无登录则在脚唤起登录框(需要前端框架支持)

大概的记名上出接口实现:

图片 44

 1 /*
 2 无论成功与否皆会关闭登录框
 3 参数包括:
 4 success 登录成功的回调
 5 error 登录失败的回调
 6 url 如果没有设置success,或者success执行后没有返回true,则默认跳往此url
 7 */
 8 HybridUI.Login = function (opts) {
 9 };
10 //=>
11 requestHybrid({
12     tagname: 'login',
13     param: {
14         success: function () { },
15         error: function () { },
16         url: '...'
17     }
18 });
19 //与登录接口一致,参数一致
20 HybridUI.logout = function () {
21 };

图片 45

账号信息得到

当实际上的事体支付中,判断用户是否登录、获取用户核心信息的急需比比皆是,所以这边不可不管Hybrid开发模式及H5开发模式保持统一,否则要以事情代码中举行多无谓的论断,我们在前者框架会卷入一个User模块,主要接口包括:

1 var User = {};
2 User.isLogin = function () { };
3 User.getInfo = function () { };

此代码的平底实现分为前端实现,Native实现,首先是前者的做法是:

眼下端页面载入后,会做相同差异步请求,请求用户相关数据,如果是登录状态就会获取数据存于localstorage中,这里毫无疑问非克存取敏感信息

前端采用localstorage的说话需要考虑极端气象下下内存变量的道替换localstorage的兑现,否则会产出不足利用的情,而连续的顾皆是动localstorage中之数量做判断依据,以下情形用清理localstorage的账号数据:

① 系统上

② 访问接口提示用登录

③ 调用登录接口

这种模式多用来单页应用,非单页应用一般会在每次刷新页面先清空账号信息更异步拉取,但是一旦手上页面就便得看清用户登录数以来,便不可因了;处于Hybrid容器中经常,因为Native本身即保留了用户信息,封装的接口直接由Native获取即可,这块比较靠谱。

Hybrid的资源

Hybrid的资源

目结构

Hybrid技术既是用静态资源存于Native,那么尽管需目录设计,经过前的阅历,目录结构相似为2层目录划分:

图片 46

苟我们发个别独频道酒店和机票,那么目录结构是这般的:

webapp //根目录 ├─flight ├─hotel //酒店频道 │ │ index.html
//业务入口html资源,如果未是单页应用会有差不多个入口 │ │ main.js
//业务所有js资源打包 │ │ │ └─static //静态样式资源 │ ├─css │ ├─hybrid
//存储业务定制化类Native Header图标 │ └─images ├─libs │ libs.js
//框架所有js资源打包 │ └─static ├─css └─images

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
webapp //根目录
├─flight
├─hotel //酒店频道
│  │  index.html //业务入口html资源,如果不是单页应用会有多个入口
│  │  main.js //业务所有js资源打包
│  │
│  └─static //静态样式资源
│      ├─css
│      ├─hybrid //存储业务定制化类Native Header图标
│      └─images
├─libs
│      libs.js //框架所有js资源打包
└─static
    ├─css
    └─images

最初计划之forward跳反负之topage参数规则是:频道/具体页面=>channel/page,其余资源会由于index.html这个进口文件带起。

目录结构

Hybrid技术既是将静态资源存于Native,那么就算得目录设计,经过前的涉,目录结构相似为2层目录划分:

图片 47

如我们有少数单频道酒店与机票,那么目录结构是这么的:

图片 48

 1 webapp //根目录
 2 ├─flight
 3 ├─hotel //酒店频道
 4 │  │  index.html //业务入口html资源,如果不是单页应用会有多个入口
 5 │  │  main.js //业务所有js资源打包
 6 │  │
 7 │  └─static //静态样式资源
 8 │      ├─css 
 9 │      ├─hybrid //存储业务定制化类Native Header图标
10 │      └─images
11 ├─libs
12 │      libs.js //框架所有js资源打包
13 │
14 └─static
15     ├─css
16     └─images

图片 49

初期设计之forward跳反受的topage参数规则是:频道/具体页面=>channel/page,其余资源会由于index.html这个进口文件带出。

增量机制

真实性的增量机制亟待服务器端的相当,我此不得不简单描述,Native端会生出维护一个版本映射表:

JavaScript

{ flight: 1.0.0, hotel: 1.0.0, libs: 1.0.0, static: 1.0.0 }

1
2
3
4
5
6
{
  flight: 1.0.0,
  hotel: 1.0.0,
  libs: 1.0.0,
  static: 1.0.0
}

其一映射表是历次大版本APP发布时由服务器端生成的,如果酒馆频道要在线做增量发布以来,会卷入一个与丝上亦然的文件目录,走发布平台公布,会当数据库被形成一致久记下:

channel ver md5
flight 1.0.0 1245355335
hotel 1.0.1 455ettdggd

 

当APP启动时,APP会读取版本信息,这里发现hotel的地面版本号比线上的有点,便会下载md5对应之zip文件,然后解压之又替换整个hotel文件,本次增量结束,因为有的版本文件不见面又,APP回滚时可用回到任意想去的本,也堪对擅自版本做BUG修复。

增量机制

实的增量机制亟待劳务器端的相当,我此只能简单描述,Native端会出护一个版映射表:

{
  flight: 1.0.0,
  hotel: 1.0.0,
  libs: 1.0.0,
  static: 1.0.0
}

其一映射表是每次大版本APP发布时由服务器端生成的,如果酒店频道要在线做增量发布以来,会卷入一个和线上同一的文件目录,走发布平台公布,会以数据库被形成相同修记下:

channel ver md5
flight 1.0.0 1245355335
hotel 1.0.1 455ettdggd

 

 

 

当APP启动时,APP会读取版本信息,这里发现hotel的地头版本号比线上之略微,便会下载md5对应的zip文件,然后解压之以替换整个hotel文件,本次增量结束,因为具备的本子文件未会见还,APP回滚时可用回到任意想去之版,也可本着擅自版本做BUG修复。

结语

github上代码会连更新,现在界面反正不极端好看,大家多多包涵吧,这里是局部职能图:

图片 50图片 51图片 52

Hybrid方案是全速迭代项目,快速占领市场的神器,希望此文能对准备接触Hybrid技术之情侣提供部分救助,并且更感谢明月同窗的相当。

 

1 赞 4 收藏
评论

图片 53

结语

github上代码会不断创新,现在界面反正不太好看,大家多多包涵吧,这里是部分功能图:

图片 54图片 55图片 56

Hybrid方案是全速迭代项目,快速占领市场的神器,希望此文能对准备接触Hybrid技术之对象提供部分帮,并且更感谢明月同学的配合。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图