| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 移动开发 -> 干货 | 携程APP Native/RN内嵌Flutter UI混合开发实践和探索 -> 正文阅读 |
|
[移动开发]干货 | 携程APP Native/RN内嵌Flutter UI混合开发实践和探索 |
作者简介 Deway,携程资深工程师,iOS客户端开发,热衷于大前端和动态化技术; Frank,携程高级工程师,关注移动端热门技术,安卓客户端开发。 前言 随着各种多端技术的蓬勃发展,如今的移动端和前端早已不再拘泥于自身的边界,而是不断延伸、扩展和融合,逐步向着真正的大前端技术迈进。跨端技术也从早期的Cordova/PhoneGap、纯H5页面发展到如今的ReactNative(以下简称RN)、Weex、小程序、Flutter群雄并存的局面。各种技术栈各有优劣和特点,技术选型需视团队自身情况而定,没有绝对好坏之分。然而在实际开发中,并不是只选用一种技术栈,那么研究多种技术栈融合和嵌套使用的就有了迫切的必要性。 本文我们从实际业务场景出发,初步实践了在RN里面嵌套flutter view、在native里面嵌套flutter view,探索其可行性,并回顾这个过程中遇到的一些问题和解决方案。 一、背景 1.1 现状 随着时间的推移,携程app中酒店列表和详情两大页面已经全部转为flutter技术栈,初期的使用场景也比较单一,只在主流程使用。然而业务不断迭代之后,flutter页面在其他流程使用的频次也越来越高,比如列表页面,作为酒店一线SKU产品展示的主页面,复用的需求非常旺盛和迫切。那么此时需要思考更多的通用性和可移植性,以适用于在不同的场景不同的技术栈页面嵌入使用。 1.2 两大场景
对于不同技术栈的业务场景,不断为多侧业务同步补齐功能,维护成本是相当巨大的。对于酒店列表业务来说,唯一可选的路径就是在大搜和酒店主频道等业务场景中共用一个列表,甩开历史包袱,实现真正意义上的业务对齐。所以,基于以上两个场景,我们初步探索了flutter页面在多种复杂结构的嵌套使用,即RN中嵌套flutter、原生ListView中嵌套flutter,并将解决方案记录在本文中,为之后可能遇到的多业务场景提供一个思路。 二、RN中使用Flutter 2.1 可行方案的探究 在接到这个嵌套需求的时候,考虑到成本最低的方式是直接在大搜页面层上盖列表,即在切换到酒店tab的时候将flutter view盖在上层。实际上在思考利弊之后,放弃了这个方案。有如下几个弊端:
基于上述的几个问题,那么考虑的方向就偏向于直接把flutter view包装成RN的Component使用。 2.2 Native Components的原理 我们先简单回顾下RN的启动流程(以iOS为例)。 RN启动流程
这里省去了一些非关键步骤,可以看到RN本身是支持调用native原生组件的,调用native UI components这一步比较关键的是RCT_EXPORT_MODULE。这是一个宏定义,重写了load方法,在其中调用RCTRegisterModule方法。再看看RCTRegisterModule的实现,其实就是将moduleClass注册到一个全局容器。
moduleClass注册完之后什么时候使用呢?接下来就到RCTCxxBridge的start方法, 将所有注册的组件放入moduleClasses,并将继承于RCTViewManager的module单独拿出来再处理。
从头到尾来理解下,在main函数开始执行之前,将申明为RCT_EXPORT_MODULE的组件注册到全局容器中,并在bridge中生成RCTViewManager对应的RCTComponentData对象,并配置moduleConfig的模块信息表(上述步骤4中完成)。然后在RCTUIManager中建立和js布局层的对应关系,最后在js层进行计算、排版之后通过UIManager.js通知到native层的RCTUIManager进行渲染绘制。这就是一个RN使用Native原生组件的原理和过程,由此可以见RN对于modules层的设计具备高度可扩展性和伸缩性。 2.3 前置条件 2.3.1 组件生命周期 携程主站是一个包含native、RN、H5、flutter技术栈的混合app,基础框架由native代码实现,因此flutter业务需要依赖于兼容native、flutter的技术框架,业内比较成熟的解决方案是FlutterBoost。 FlutterBoost的理念是将flutter像Webview那样来使用,通过native容器来管理flutter页面。类似的,携程app中RN技术栈也是一个RN-native混合方案CRN,用native容器封装了RN页面。这样的方案可以实现一个native容器中同时嵌套native、RN、flutter组件,并由native容器管理生命周期。 那flutter-RN组件嵌套时,如何实现不同组件生命周期相关联?由于目前列表flutter view是依附列表控制器存在的,在创建RN对应的列表控制器view时,将flutter view的控制器挂载到父控制器,这样实现了flutter view依赖RN的生命周期,伪代码如下。
从官方手册可知,
Android的实现类似,从xml文件可以看出,同样是将flutter view挂载到RN父ViewGroup中,即RNLinearLayout。
小结一下,flutter的生命周期可以依赖于嵌入的父组件,如下表所示。
2.3.2 flutter页面启动方式 由FlutterBoost官方文档可知,flutter页面以路由的方式启动,携程app中实现(以Android为例)如下代码段所示。启动时需配置一个flutter url,包含页面类型、业务参数、UI相关参数等,用一个fragment来管理view,并在fragment的生命周期不同阶段完成flutter初始化、绘制、销毁等操作,伪代码如下。
2.4 开始 从前述原理来看,native的UI组件直接遵守RCTViewManager的模式提供view方法就可以被RN调用。那么是不是flutter view的嵌入也可以遵从这套范式呢?顺着这个思路设计结构图如下: rnFlutter混合结构图
js层包装类如下:
下面罗列主体结构的部分代码。iOS的实现部分伪代码如下:
Android平台上对于flutter view嵌入RN容器有相似的流程。首先需要在RN初始化时创建ViewReactPackage,它会提供给RN一个RNViewManager,伪代码如下:
RNViewManager在RN里注册嵌入native模块的名字、layout、RN和native通信接口实现。native模块的名字需要与RN中的RCT_EXPORT_MODULE名字、iOS native模块的名字对应。command接口实现了相关业务逻辑,比如initFlutterFragment()方法中创建flutter view,其它command接口中实现了目的地关键字、入离日期以及业务埋点数据等等。
RN native layout有一些特殊,由于flutter view自身是一个framelayout,RN native layout定义为一个子view是framelayout的linearlayout,这样可以实现动态地在RN native viewGroup中加入flutter view。根据官方文档,RN native view需要覆写requestLayout()方法,并在方法中重新做测量和布局,伪代码如下:
考虑到flutter view并不是单一场景使用,比如上述1.2节的场景二,需要在酒店查询页移植列表页。最终的结构设计如下图,Native层对应具体的业务创建对应的EmbedderPlugin,负责处理相关的事件,flutter层在不同路由下对应具体的page。 flutterEmbedder结构 三、Native嵌套Flutter 3.1 可行方案的探究 从view树的角度,RN嵌套flutter的实现和native嵌套flutter的实现是一致的。RN嵌套flutter时,flutter view作为一个view group加入到RN container中,而native嵌套flutter时,flutter view作为一个view group直接加入到native view树中。这样的实现需要考虑四个要点:点击事件传递、view启动顺序、flutter层与native层的业务交互、页面的生命周期。 3.2 方案实现 3.2.1 点击事件传递 处理点击事件传递,flutter view作为一颗view子树,能够直接接受到从上到下传递的点击事件。点击事件传递过程如下左图所示,在flutter点击区域由flutter处理事件,若flutter不处理则回到父view处理。 ? flutter点击事件 flutter滑动事件 list滚动事件则需要在flutter view子树的祖先view中进行适当屏蔽,确保flutter列表能嵌套滚动。本次实现的业务场景是1.2节中的场景二,在一个native滚动列表最下方嵌入flutter滚动列表,flutter滚动列表正好能占满一个屏幕。整个列表向下滚动过程中,先滚动外层列表,当滚动到底部时滚动flutter列表;反之,整个列表向上滚动过程中,先滚动flutter列表,当flutter列表滚动到头部时滚动,向上滚动外层列表。 如上图所示,滑动过程(1)是flutter列表可滑动场景,需要将事件返回外层列表;滑动过程(2)是列表可滑动场景;滑动过程(3)是flutter列表不可上滑,而外层列表可上滑场景,此时需要将事件传递到外层列表使其上滑,这个过程中会有顿挫感,我们在实现中给外层列表添加了滑动效果进行补偿。 3.2.2 view启动顺序 通常是先创建native view树,在view树创建成功后,手动创建flutter view并加入view树中。手动创建flutter view可以根据业务需要,以懒加载的方式创建。在app启动之后,不管是否启动flutter view,都需要先初始化flutter引擎。 3.2.3 flutter-native业务交互 业务在flutter层与native层的交互,主要通过flutter method channel,在native层预先注册method channel和各种事件,在flutter view启动之后由flutter层或native层双向发送消息。 3.2.4 页面的生命周期 生命周期已在2.3.1节中详细描述,可以由native层容器或者flutter view来控制,通常是根据业务所占页面比例决定,我们的实现中是将flutter view包在一个native容器中,这样可以用相同的方法在native控制生命周期。 四、写在最后 至此我们初步探索了不同技术栈UI的嵌套,为之后的组件的复用提供了一套切实可行的实践依据。后续会在此基础上做进一步的优化,比如flutter view的滚动事件如何很平滑地传输到native,使得双列表嵌套滚动的时候没有顿挫感。在实践中,随着组件复杂度的和依赖度升高,混合的改造成本也是逐步增加的,那么是否需要混合、如何轻量化的移植也是需要进一步衡量和思考的。 参考文献
团队招聘信息 我们是携程酒店前端研发团队,负责携程Ctrip APP/国际版Trip APP/小程序/Web端的酒店业务研发和创新。保障酒店业务高效产出,稳定迭代的同时,持续优化用户体验和优化酒店预订流程。 在酒店前端研发团队,有包容性,多样性的团队文化,你可以和一群纯真,富有激情的年轻人一起工作,为全球旅行者的提升旅行体验。 如果你热爱技术,并渴望不断成长,携程酒店前端研发团队期待您的加入。目前我们Android/IOS/RN/Flutter/H5/服务等领域均有开放职位。简历投递邮箱:tech@ctrip.com,邮件标题:【姓名】-【携程酒店前端】-【投递职位】。 【推荐阅读】 ?“携程技术”公众号 ? 分享,交流,成长 |
|
移动开发 最新文章 |
Vue3装载axios和element-ui |
android adb cmd |
【xcode】Xcode常用快捷键与技巧 |
Android开发中的线程池使用 |
Java 和 Android 的 Base64 |
Android 测试文字编码格式 |
微信小程序支付 |
安卓权限记录 |
知乎之自动养号 |
【Android Jetpack】DataStore |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 0:46:19- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |