作者简介

姜睿东,2009年加入携程,从事无线研发,现在大住宿事业群负责酒店无线研发工作。

一直以来,Native App因为审核的原因,新版本不能很及时地上线。尤其是iOS,碰到点审核问题,有时候一连几天都不能上架,严重影响业务和产品的体验。

大家一直都在寻求能够动态更新业务的方法,关于这方面的框架也是层出不穷。自从Facebook推出React Native以后,便以其良好的兼容性和性能优势占据了这方面的领先地位,携程也在此基础上开源了CRN框架。

如果是新业务,用CRN开发是非常合适的,开发效率高,双平台兼容性好。但如果要把已有的Native页面转CRN,复杂的核心页面成本会有点高。在不增加人手的情况下,要想同时进行业务的迭代和CRN的转换,会有点力不从心。

如果硬转,周期会很长。以携程酒店主流程页面之一的订单详情页为例,在没有额外增加人手的情况下,前后花了几个月时间,才陆陆续续完成了90%的功能转CRN,过程尤为艰辛。订单详情页是主流程页面中相对简单的,如果要转酒店详情页,光是几百行的ViewModel就已经让人望而却步了。

对此,我们考虑能不能采用一种让Native和CRN共存的方式,这样既可以保留Native的业务逻辑,又可以在UI层面做到灵活应变。最关键的是,可以分模块的开发,而不用像转CRN那样必须整个页面一起上。

当然,Native和CRN混合的解决方案早就有了,但是当CRN作为一个子View出现在Native页面里的时候,由于CRN的框架比较重量级,在性能上并不是特别理想,而且和Native的交互也不是特别方便,所以我们开始考虑有没有更为轻便的解决方案

在比较了多种跨平台方案之后,首先排除了类似Lua这种需要依赖第三方库,且语法非主流的方案,最终决定采用原生系统就自带支持的,且语法有着广泛群众基础的JavaScript。

在iOS7之前,要在Native环境中和JavaScript交互是非常简单且功能有限的,基本上只有依靠Webview的EvaluateJavaScript 来注入执行一段JS脚本。从iOS7开始,苹果引入了JavaScriptCore这个库,顿时给iOS的开发带来了翻天覆地的变化。

为什么会这么说呢,首先来看一下JavaScriptCore中所包含的两个关键类,JSContext和JSValue:

JSContext

JSContext提供了一个在APP中执行JavaScript代码的环境,使得我们可以直接在Objective-C或Swift代码中直接调用JavaScript代码,并得到返回结果,反过来也可以暴露方法和类供JavaScript调用。

JSValue

JSValue则是一个JavaScript数据类型在Objective-C或Swift中的包装对象,借助于这个对象我们可以在Native代码和JavaScript代码之间互相传值,这两者之间的对应关系如下图所示:

Objective-C (and Swift) Types

JavaScript Types

nil

undefined

NSNull

null

NSString (Swift String)

String

NSNumber and primitive numeric types

Number, Boolean

NSDictionary (Swift Dictionary)

Object

NSArray (Swift Array)

Array

NSDate

Date

Objective-C or Swift object (id or AnyObject)

Objective-C or  Swift class (Class or AnyClass)

Object

Structure types:

NSRange, CGRect, CGPoint, CGSize

Object

Objective-C block (Swift closure)

Function

简单总结一下,JSContext提供JavaScript和Native互相调用的接口,JSValue提供互相调用之间的数据类型转换,这样的调用方法比之前的Webview要强大灵活许多,想象空间也大了很多。所以我们接下去就准备在这基础之上做点文章。

第一步,先创建一个JavaScript对象,用来描述对应iOS中的UIView,代码用ES6如下:

Class View {    constructor() { this.x = 0;    this.y = 0;    this.width = 0;    this.height = 0;   this.borderWidth = 0;  this.borderColor = ‘’; this.cornerRadius = 0; this.masksToBounds = false;    this.subviews = [];    }   initWithFrame(x, y, width, height) {    ……  }   addSubview(v) { ……      }   setOnclick(click) { ……      }   ……
}

这些属性和方法都是iOS中UIView比较常用的,如同在iOS中UILabel是继承自UIView一样,我们继续创建一个JavaScript的Label对象,并继承自刚才在上面创建的View对象。

Class Label extends View { constructor() { super();    this.text = ‘’;    this.textColor = ‘’;   this.textSize = 14;    this.fontStyle = 0;    this.textAlignment = 0;    this.lineBreakMode = 4;    this.numberOfLines = 1;    }
}

以此类推,我们继续创建诸如Imageview,Button,ScrollView等iOS中常用的组件,只要愿意,所有的组件都可以用这种方式来描绘。

有了这些基础的JavaScript组件,接下去就可以如同在iOS中布局一样,开始用这些组件进行布局,如下代码片段示例了如何对一张图片进行布局。

createImage() {   var container = View.initWithFrame(0, 0, 50, 50);  container.backgroundColor = "#FFFFFF";   var image = Image.initWithFrame(0, 0, 50, 50); image. imageUrl = ‘http://m.ctrip.com/xxxxx.png’;  container.addSubView(image);    return container;   }

对于熟悉iOS开发的同学来说,会觉得这段代码非常眼熟。没错,这就是一段用JavaScript来写的iOS代码,依此类推,稍微复杂一点的布局也可以用这种方式完成。

最后来看一下布局完成以后的返回值,暂时还是先以上面的Image控件来做示例:

render() {  varcontainer = View.initWithFrame(0, 0, 50, 50);   container.backgroundColor = "#FFFFFF";   var image = Image.initWithFrame(0, 0, 50, 50); image. imageUrl = ‘http://m.ctrip.com/xxxxx.png’;  container.addSubView(image);    var demoView = View.initWithFrame(0,0,180,180);    demoView.addSubView(container)  return demoView;
}

如果在浏览器或者JavaScript环境中运行上述代码,会得到一个自定义的递归对象,根对象会包含一个Subview数组,数组中的每个元素都有可能是另外一组UI对象,当然实际操作中并不建议层次太多,一般1-2层。

做到这里,JavaScript的部分暂告一段落。接下来回到Native当中,还记得上文提到的JSContext么?这是一个在Native当中的JavaScript执行环境,我们在Native环境中用JSContext来执行刚才那个Demo,就会得到一个对应的JSValue值,这个JSValue的值用[JSValuetoObjct]来转换成Object-C对象的话,最终就得到了一个字典,NSDictionary。

继续递归地拆解这个字典,拆解到底,每个元素最终都会转成OC的Object,然后根据每个Object预先定义好的Type类型,实例化成相应的Native组件,并且每个组件有一个对应的数据Model。

还是以上述那个Label为例,其对应的OC Label代码如下:

@implementation Label    - (void)setModel:(HTLDynamicLabelModel *)model{ self.dynamicViewModel = model; self.text = model.text;    self.textColor = model.textColor;  self.font = model.font;    self.lineBreakMode = model.lineBreakMode;  self.numberOfLines = model.numberOfLines;  if(model.richText && model.richText.attributedString) { self.attributedText = model.richText.attributedString; }
}   @end

到此为止,就完成了所有之前在JavaScript中描绘的控件在Native里的转换,剩下的事情就是对这些Native组件进行渲染了,具体就不在这里描述了。

总体来说,这个思路在原理上跟RN或者CRN是一样的,但更为轻量一点,几乎0配置就能使用。通过配置增量更新,从服务端下载最新的JS文件,可以做到类似CRN在线更新的效果。

从性能上来看,因为不需要额外加载任何框架代码,JS执行的消耗几乎可以忽略,所以和Native混合在一起的时候,几乎看不出有任何延迟。

这个方案非常适合做一些轻量级的又需要经常不定期更新的UI,比如节日氛围或者城市包装的UI。这些UI经常会跟随节假日更新,用这个方案可以轻松在线更新UI,不用通过服务端下发一堆样式来控制,减轻了服务发布的压力和不必要的服务交互。

综上所述,这是我们团队对新事物的一些探讨和研究,并不存在要代替CRN或其他框架一说,每个框架都有其适用的场景,没有绝对的优劣之分。

在研究这个解决方案的过程中,我们也认真地深入了解了JavaScriptCore的一些机制,原理都是万变不离其宗的,但可以结合不同的场景,进行不同的演变,就看怎么灵活运用了。

所以,与其说本文是在探索iOS中动态View的解决方案,也不妨说成是对JSContex和JSValue如何运用的一些探讨,从实际的摸索中来看,灵活运用好JavaScriptCore,可以有无限多的可能。

【推荐阅读】


  • 近万字长文详述携程大规模应用RN的工程化实践

  • Electron在DevTools中的探索与实践

  • 浅谈Node.js在携程的应用

  • 云计算时代携程的网络架构变迁

  • 携程酒店小程序开发背后的“黑科技”

干货 | 携程酒店iOS动态View的探索相关推荐

  1. 干货 | 携程酒店安卓地图开发实践

    作者简介 亦枫,携程资深软件工程师,负责酒店业务 Android 客户端的相关研发工作. 当前大多数移动互联网 App 都会存在地图相关功能,尤其是 LBS(基于位置服务)相关的业务,依赖性更强,携程 ...

  2. 干货 | 携程酒店Flutter性能优化实践

    作者简介 Qifan,携程高级工程师,专注移动端开发:Yinuo,携程高级工程师,专注移动端开发:popeye,携程软件技术专家,关注移动端跨端技术,致力于快速,高性能地支撑业务开发. 一 .前言 携 ...

  3. 干货 | 携程酒店MOCK全链路实践

    作者简介 刘晓攀,携程酒店性能测试负责人,专注性能测试分析和辅助测试工具的开发. 一.前言 Mock在整个软件开发测试周期中已经非常普遍,我们也会经常有意无意地使用它.譬如开发了一段代码,这段代码强依 ...

  4. 干货 | 携程酒店RSocket实践

    作者简介 刘诚,携程酒店研发性能架构师.2014年加入携程,致力于通过架构的演进,控制企业硬件成本. 一.初识RSocket 在QCon2019北京大会上第一次得知RSocket.印象深刻的是Neti ...

  5. 干货 | 携程酒店小程序开发背后的“黑科技”

    作者简介 崔广宇,携程酒店研发部小程序开发经理,曾负责过反爬虫开发以及H5开发. 本文将分享携程酒店小程序的一些开发经验, 和一些非技术的经验.这里的小程序包括微信小程序,支付宝,百度,头条.快应用因 ...

  6. 干货 | 携程酒店实时数仓架构和案例

    作者简介 秋石,携程数据仓库专家,关注大数据.数据仓库.数据治理等领域: 九号,携程数据技术专家,关注数据仓库架构.数据湖.数据治理: 魁伟,携程资深数据工程师,关注实时&离线大数据产品及技术 ...

  7. 干货 | 携程酒店搜索引擎AWS上云实践

    作者简介 宮娴,携程高级后端开发工程师:Spike,携程高级后端开发专家. 随着携程国际化业务的快速推进,搜索引擎作为用户体验中至关重要的一环,上云变得志在必行.本文主要分享酒店搜索引擎迁移AWS的探 ...

  8. 干货 | 携程机票Sketch插件开发实践

    作者简介 尹正波,携程机票研发部前端工程师,专注设计和开发的交叉领域,用系统和工具改进设计体验和交付. Sketch 是伴随移动应用程序崛起而流行的 UI 设计工具.2014年 Sketch V3 增 ...

  9. 干货 | 从47%到80%,携程酒店APP流畅度提升实践

    作者简介 Jin,携程高级研发经理,专注移动技术开发:Dan,携程测试开发经理,关注数据挖掘以及数据在系统质量提升中的应用:Lanbo,携程软件技术专家,专注移动技术开发. 一.背景 APP性能提升一 ...

最新文章

  1. 为什么一般要定义析构函数为虚析构函数
  2. 从员工出走仅剩 5 人,到一支打胜仗的铁军
  3. dos中特殊符号命令的应用
  4. Give root password for maintenance 问题解决.
  5. java将json转为hashmap_将JSON字符串转换为HashMap
  6. 高文院士:从“乡村教师”到人工智能掌舵者的40年科研路
  7. 让Ubuntu更多的使用物理内存
  8. vscode+vim使用技巧
  9. android file transfer下载_PHP通过header方式下载文件
  10. oracle打印awr报告,oracle生成awr报告
  11. for循环遍历Set集合时如何判断是否有下一个元素
  12. 对一些稀奇古怪面试题的理解
  13. wps文字如何取消英文首字母输入时自动变大写
  14. 刷入twrp_twrp刷入面具进入recovery(twrp)的方式获取root刷入第三方rom获取第三方rom包类原生rom包的网络连接受限问题
  15. PhotonServer教程《五》
  16. ExecutorExecutorService
  17. css向上三角,css实现三角
  18. Task5样式色彩秀芳华
  19. python入门笔记(1)
  20. 技术科普丨解密无处不在的EMC干扰

热门文章

  1. C++ 压缩文件夹(一)
  2. android 自带网络共享,安卓手机如何共享自身网络
  3. 【天下】探访欧洲最人性化监狱
  4. 学计算机买哪种手机好,想购买手机〜您能推荐哪款手机吗?因为是学生
  5. 小米VR一体机游戏开发日记(第二天)
  6. 服务器中电池可以维修吗,电池修复到底有没有效果?一个维修工的遭遇揭开真相!...
  7. JavaScript 函数式编程——入门指南
  8. ChatGPT开源平替(1)——ChatGLM
  9. IOS 申请证书 出现用户名密码错误的解决
  10. android 投屏 app 三星,[图]为何Windows 10的Android应用投屏功能仅限于三星手机?