根据之前的介绍,大家对前端与Native的交互应该有一些简单的认识了,很多朋友就会觉得这个交互很简单嘛,其实并不难嘛,事实上单从Native与前端的交互来说就那点东西,真心没有太多可说的,但要真正做一个完整的Hybrid项目却不容易,要考虑的东西就比较多了,单从这个交互协议就有:
① URL Schema
② JavaScriptCore
两种,到底选择哪种方式,每种方式有什么优势,都是我们需要深度挖掘的,而除此之外,一个Hybrid项目还应该具有以下特性:
① 扩展性好——依靠好的约定
② 开发效率高——依赖公共业务
③ 交互体验好——需要解决各种兼容问题
我们在实际工作中如何落地一个Hybrid项目,如何推动一个项目的进行,这是本次我们要讨论的,也希望对各位有用。
文中是我个人的一些开发经验,希望对各位有用,也希望各位多多支持讨论,指出文中不足以及提出您的一些建议。
设计类博客
http://www.cnblogs.com/yexiaochai/p/4921635.html
http://www.cnblogs.com/yexiaochai/p/5524783.html
http://www.cnblogs.com/nildog/p/5536081.html#3440931
Android博客
https://home.cnblogs.com/u/vanezkw
代码地址:https://github.com/yexiaochai/Hybrid
因为IOS不能扫码下载了,大家自己下载下来用模拟器看吧,下面开始今天的内容。
总体概述在第一章,有兴趣大家去看
细节设计在第二章,有兴趣大家去看
本章主要为打补丁
边界问题
在我们使用Hybrid技术前要注意一个边界问题,什么项目适合Hybrid什么项目不适合,这个要搞清楚,适合Hybrid的项目为:
① 有60%以上的业务为H5
② 对更新(开发效率)有一定要求的APP
不适合使用Hybrid技术的项目有以下特点:
① 只有20%不到的业务使用H5做
② 交互效果要求较高(动画多)
任何技术都有适用的场景,千万不要妄想推翻已有APP的业务用H5去替代,最后会证明那是自讨苦吃,当然如果仅仅想在APP里面嵌入新的实验性业务,这个是没问题的。
交互约定
根据之前的学习,我们知道与Native交互有两种交互:
① URL Schema
② JavaScriptCore
而两种方式在使用上各有利弊,首先来说URL Schema是比较稳定而成熟的,如果使用上文中提到的“ajax”交互方式,会比较灵活;而从设计的角度来说JavaScriptCore似乎更加合理,但是我们在实际使用中却发现,注入的时机得不到保障。
iOS同事在实体JavaScriptCore注入时,我们的原意是在webview载入前就注入所有的Native能力,而实际情况是页面js已经执行完了才被注入,这里会导致Hybrid交互失效,如果你看到某个Hybrid平台,突然header显示不正确了,就可能是这个问题导致,所以JavaScriptCore就被我们弃用了。
1 2 3 |
JavaScriptCore可能导致的问题: ① 注入时机不唯一(也许是BUG) ② 刷新页面的时候,JavaScriptCore的注入在不同机型表现不一致,有些就根本不注入了,所以全部hybrid交互失效 |
如果非要使用JavaScriptCore,为了解决这一问题,我们做了一个兼容,用URL Schema的方式,在页面逻辑载入之初执行一个命令,将native的一些方式重新载入,比如:
1 2 3 |
_.requestHybrid({ tagname: 'injection' }); |
这个能解决一些问题,但是有些初始化就马上要用到的方法可能就无力了,比如:
① 想要获取native给予的地理信息
② 想要获取native给予的用户信息(直接以变量的方式获取)
作为生产来讲,我们还是求稳,所以最终选择了URL Schema。
明白了基本的边界问题,选取了底层的交互方式,就可以开始进行初步的Hybrid设计了,但是这离一个可用于生产,可离落地的Hybrid方案还比较远。
账号体系
一般来说,一个公司的账号体系健壮灵活程度会很大程度反映出这个研发团队的整体实力:
① 统一的鉴权认证
② 短信服务图形验证码的处理
③ 子系统的权限设计、公共的用户信息导出
④ 第三方接入方案
⑤ 接入文档输出
⑥ ……
这个技术方案,有没有是一回事(说明没思维),有几套是一回事(说明比较乱,技术不统一),对外的一套做到了什么程度又是一回事,当然这个不是我们讨论的重点,而账号体系也是Hybrid设计中不可或缺的一环。
账号体系涉及了接口权限控制、资源访问控制,现在有一种方案是,前端代码不做接口鉴权,账号一块的工作全部放到native端。
native代理请求
在H5想要做某一块老的App业务,这个APP80%以上的业务都是Native做的,这类APP在接口方面就没有考虑过H5的感受,会要求很多信息如:
① 设备号
② 地理信息
③ 网络情况
④ 系统版本
有很多H5拿不到或者不容易拿到的公共信息,因为H5做的往往是一些比较小的业务,像什么个人主页之类的不重要的业务,Server端可能不愿意提供额外的接口适配,而使用额外的接口还有可能打破他们统一的某些规则;加之native对接口有自己的一套公共处理逻辑,所以便出了Native代理H5发请求的方案,公共参数会由Native自动带上。
1 2 3 4 5 6 7 8 9 10 11 12 |
//暂时只关注hybrid调试,后续得关注三端匹配 _.requestHybrid({ tagname: 'apppost', param: { url: this.url, param: params }, callback: function (data) { scope.baseDataValidate(data, onComplete, onError); } }); |
这种方案有一些好处,接口统一,前端也不需要关注接口权限验证,但是这个会带给前端噩梦!
1 |
前端相对于native一个很大的优点,就是调试灵活,这种代理请求的方式,会限制请求只能在APP容器中生效,对前端调试造成了很大的痛苦 |
从真实的生产效果来说,也是很影响效率的,容易导致后续前端再也不愿意做那个APP的业务了,所以使用要慎重……
注入cookie
前端比较通用的权限标志还是用cookie做的,所以Hybrid比较成熟的方案仍旧是注入cookie,这里的一个前提就是native&H5有一套统一的账号体系(统一的权限校验系统)。
因为H5使用的webview可以有独立的登录态,如果不加限制太过混乱难以维护,比如:
我们在qq浏览器中打开携程的网站,携程站内第三方登录可以唤起qq,然后登录成功;完了qq浏览器本来也有一个登录态,发现却没有登录,点击一键登录的时候再次唤起了qq登录。
当然,qq作为一个浏览器容器,不应该关注业务的登录,他这样做是没问题的,但是我们自己的一个H5子应用如果登录了的话,便希望将这个登录态同步到native,这里如果native去监控cookie的变化就太复杂了,通用的方案是:
1 |
Hybrid APP中,所有的登录走Native提供的登录框 |
每次打开webview native便将当前登录信息写入cookie中,由此前端就具有登录态了,登录框的唤起在接口处统一处理:
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 () { }; |
账号切换&注销
账户注销本没有什么注意点,但是因为H5 push了一个个webview页面,这个重新登录后这些页面怎么处理是个问题。
我们这边设计的是一旦重新登录或者注销账户,所有的webview都会被pop掉,然后再新开一个页面,就不会存在一些页面展示怪异的问题了。
公共业务的设计-体系化
在Hybrid架构中(其实就算在传统的业务中也是),会存在很多公共业务,这部分公共业务很多是H5做的(比如注册、地址维护、反馈等,登录是native化了的公共业务),我们一个Hybrid架构要真正的效率高,就得把各种公共业务做好了,不然单是H5做业务,效率未必会真的比Native高多少。
底层框架完善并且统一后,便可以以规范的力量限制各业务开发,在统一的框架下开发出来的公共业务会大大的提升整体工作效率,这里以注册为例,一个公共页面一般来说得设计成这个样子: