10 月 23 - 25 日,QCon 上海站即将召开,现在购票,享9折优惠 了解详情
写点什么

React Native 优先的多端统一化方案

  • 2020-12-27
  • 本文字数:3096 字

    阅读完需:约 10 分钟

React Native 优先的多端统一化方案

一、方案背景


长期以来 APP、H5、小程序等各个端的定位和发展历程都不一样,各端技术栈差异性也较大,基于成本和效率考虑并不追求各端一致性,结果就是各端真的就渐行渐远了。


移动端增量红利越来越少的情况下,产品这边逐渐追求各端的产品体验一致性,多端同时上的需求越来越多,但是由于技术上割裂较大,工时基本都会按端加倍,开发成本奇高,迫切的需要一套减少多端开发成本的方案。


二、方案调研


开始之前我们对业界现有的一些跨端方案进行了简单的调研和了解。



通过对比目前的多端开发主要有以下几个大方向:


  1. 对 IOS 和 ADR 的 native 端 APP 的适配

  2. 中国特色的针对各小程序平台的适配

  3. 都希望可以兼顾到对 H5 的转换


而实现方向上看起来五花八门,但总体思路上主要还是两个大方向:


  1. 提供自定义 DSL 静态编译转化成目标源代码(运行体验好)

  2. 运行时适配兼容(开发体验好)


这两种方式的优缺点都非常明显(相关框架和方案对比文章都较多,可自行详细了解)。目前社区基本都想在运行上努力,开发当然要自己爽啊,实际体验下来除了在微信小程序上性能问题较大之外,也没啥大毛病。


taro 作为较早的多端方案,适配性和兼容性都不错,自定义 DSL,JSX 由于其转译复杂性限制也较多。


taro-next 和 remax 都是运行时适配的方案,而 remax 的口号是 “使用真正的 react 构建小程序” (好好体会,后面要考)。


Chameleon 是号称兼容性和运行效率上都比较高的,编译和运行时适配的技术都用到了,不过由于他是支持的 weex 就没做深入尝试了。


其他像 alita,KBone,react-native-web 都在自己的小领域内能较好的运行。


三、方案介绍


1、解决思路


调研完发现上面的方案其实并不能很好的直接解决我们的问题:


Qunar 整体技术栈以 react 为主,几年前已将 APP 整体迁移到 RN 技术栈上,解决了 IOS 和 ADR 的 2 端问题。APP 的体验和开发是最重要的,不能为了多端用新的 DSL 语法来限制对 RN 技术的使用,降低 RN 的性能和开发体验。基于现状,其实我们想要的是:能直接将 RN 转换到 H5 和各小程序平台的多端方案,可惜没有。


不能直接找到一次将 RN 运行到多端的方案,其实如果能分别解决 RN 到 H5 和 RN 到小程序的问题也是能达到目标的,分析前面的跨端方案还是有一定可行性的。



2、RN 到 H5


react-native-web 是 Twitter 开源的可将 RN 代码运行在 H5 上的方案,而且他们 H5 端的官网就是直接用 RN 转出来的,自信到连自己主站都敢用的方案肯定是要尝试一下的,于是出去看了下线上效果:



发现保真度和体验还可以,对该方案的实现原理进行了解之后,觉得 H5 可行,后续到小程序的方案也深受启发。


3、react-native-web 原理


先看一行 RN 代码:



再看 react-native-web 的源代码:



使用方式:



他的方案其实挺简单粗暴的,把 RN 的组件和 API 都用 H5 实现适配一遍,适配其行为和默认样式,在打包的时候使用 webpack 的别名机制将用到的组件替换成 react-native-web 里的对应组件。


简单的方案实现起来工作量还是挺大的,所以 react-native-web 里也大量的直接使用了 react-native 的源代码,谁让他们都是 JS 呢。


既然到 H5 可以这么简单粗暴,那到小程序是否可以呢?


4、RN 到小程序


我们知道小程序都有一套自己的 DSL 语言,而且与常规前端不同的是他将逻辑层和视图层完全隔离在了两个线程里,两边数据和对象都不共享,只能通过 Native 层来传递数据通讯。


RN 广义上来说也是一样的双线程机制,不过 RN 的渲染是用的 native,小程序用的是 webkit。


RN 是完全基于 React 语言的,比小程序的 DSL 要灵活复杂得多,要把 React 语法和 JSX 编译成一个子集的话困难性还是很大的。


这个时候我们想到了 remax:使用真正的 react 构建小程序。


如果我们像 react-native-web 一样,把 react-native 组件用 remax 来实现一遍,是不是就可以将 RN 运行到小程序上了呢?


5、remax 实现原理


这部分网上有详细的原理介绍,这里只简单说一下:


react 在内部做完 VirtualDOM 的更新后最终都要调用宿主环境的更新方法(add remove)来更新界面,为了更好地对接各个平台 react 提供了 react-reconciler 库来传入真正宿主元素的更新方法(HostConfig)。基于这个库可以方便的实现出 ReactDOM、RN 等 react 渲染层。remax 就是基于该组件实现的一个 react 的渲染层,不过小程序里不能直接去更新界面,所以 reamx 会把更新收集起来,然后生成一个更新数据通过小程序的 setData 触发渲染层的更新。然后在小程序渲染层实现一个通用模板来渲染这些数据。


这个方案带来的好处和缺点,在微信小程序里的性能限制下面 remax 和 taro next 的原理介绍里都讲得很透彻,这里就不在复述了。


remax 原理介绍:


https://juejin.im/post/6844904131157557262


taro next 也采用了类似的原理:


https://juejin.im/post/6844904036743774216


6、RN 到小程序组件库


基本原理同 react-natvie-web,只不过在对 RN 组件的具体实现上采用直接使用对接到 remax 组件上的方式。看代码例子的话目录结构等是不是都和 react-native-web 很像。



四、最终多端方案


最终经过调研和尝试,我们选择了按平台用不同的方案进行转换的方案:


  1. H5 上直接用 react-native-web 进行转换

  2. 到小程序端使用 reamx 组件实现一套 RN 的组件库,借用 remax 来适配到多端

  3. 借助 webpack 的特性来实现针对不同平台的打包




qrn-mirror 就是前面提到的针对 RN 组件到小程序的组件适配库,使用 remax 实现。


我们的主要工作量集中在 qrn-mirror 层对 RN 组件的适配,借助现有开源方案小成本的实现了这个事情。


当然在实现小程序组件的时候我们才理解 remax 只说了 “使用真正的 react 构建小程序” , 从来没说过支持多端一致的,这差别你体会体会……不过幸运的是 remax 确实也通过 remax/one 提供了多个多端兼容的基础组件。当然我们最终还是选择继续使用 remax 来开发了组件库,谁让他是完整支持 react 的呢,另一个我们也不追求一次把所有小程序端都适配了。


五、多端动态方案


前面实现了将 RN 代码在多端运行的机制,但是在实际迁移中并不是所有的 RN 特性小程序和 H5 都能支持,产品和交互本身也有差异,这时候还是需要对各端做差异化和降级的。


如何做到多态其实 webpack 都已经替我们做好了,我们只需要用和定好规范就好了。


1、文件内小范围的多态


借助 webpack 的 tree shaking 和 webpack.DefinePlugin 插件,可实现按端执行逻辑且不出现冗余代码。



2、文件组件级别的多态


受益于 react 良好的组件化机制,只要我们按文件导出同样 API 的 react 组件,可以无缝的针对多端做替换。remax 也能做到自动识别小程序的原生组件。而我们只需要根据平台动态修改 webpack 查找文件后缀的优先级顺序就好了。



最终打包时在 H5 端使用 index.web.js 内导出的兼容组件,其他端用 index.js 里导出的组件。


六、效果展示


最终我们实现了一个开发工具,在规范 RN 工程目录结构的情况下可以一键转换出 H5 和小程序的版本。目前已经在低价机票,机票订单详情,公共订单列表等项目中进行了应用。后续也希望在主流程和更多新项目中直接使用。



七、后续计划


这次我们只分享了整个方案的大致思路,后续会逐步分享一些我们在实践过程中踩的一些坑和应对方案。


  1. 第三方不侵入打包过程介绍

  2. 和 nanachi 的环境整合方案

  3. react,react-native-web, remax 的版本对应和锁定问题

  4. 小程序不支持全局 global 对象的问题

  5. 小程序中 RN 动画解决方案

  6. 小程序中的渲染性能优化

  7. stylesheet 样式解决方案

  8. 多平台工程的工程结构限制

  9. 库 Size 大小分析优化过程和解决方案



头图:Unsplash

作者:冯地木

原文React Native 优先的多端统一化方案

来源:Qunar 技术沙龙 - 微信公众号 [ID:QunarTL]

转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2020-12-27 22:373523

评论

发布
暂无评论
发现更多内容

第五期(2022-2023)传统行业云原生技术落地调研报告——金融篇

York

容器 DevOps 微服务 云原生 金融

共话数字化新技术、新趋势 华为云开发者日东莞站成功举办

极客天地

系统稳定性建设之我见(64/100)

hackstoic

质量管理 系统稳定性

如何建设IT运维流程与体系

穿过生命散发芬芳

运维体系 三周年连更

基于Flutter实现Windows平台离线大模型对话应用实战

轻口味

flutter AI windows 跨平台 三周年连更

概述产品设计通用七原则

阿泽🧸

产品设计 三周年连更

Django笔记十七之group by 分组用法总结

Hunter熊

Python django count 分组查询 sum

UDP报头是通过结构体位段实现的吗

linux大本营

网络协议 udp UDP协议

6G 通信技术和 5G 通信技术的区别

汪子熙

通讯协议 通讯 三周年连更

一键生成通用的微服务(gRPC)项目代码,让你的开发效率翻倍提升

vison

Go 微服务 gRPC 代码自动生成

一文带你了解实战常用JavaScript API

程序员海军

JavaScript 三周年连更

Go sync.Once:简约而不简单的并发利器

陈明勇

Go golang 高并发 三周年连更 sync.Once

解析下rte_pktmbuf_pool_create参数含义

linux大本营

DPDK DPDK开发

dpdk中,如何建立portid/queue的配置和逻辑核心的关系

linux大本营

队列 DPDK DPDK开发

重载++运算符分别实现i++和++i

linux大本营

运算符 数据结构与算法

Go并发编程的秘密武器:内存模型和同步原语

Jack

2022-04-26:给定一个数组componets,长度为A, componets[i] = j,代表i类型的任务需要耗时j 给定一个二维数组orders,长度为M, orders[i][0]代表i

福大大架构师每日一题

golang 算法

数据存储与访问——文件存储读写

芯动大师

application 三周年连更 SharedPreference

Golang new 和 make 函数

宇宙之一粟

Go make new 三周年连更

Java - 泛型

乌龟哥哥

三周年连更

写一个完整的SHOW TABLE STATUS 语句返回的所有表的状态信息对应的结构体

linux大本营

数据库 存储 结构体 C++

nvim 配置c++环境

linux大本营

vim C++

dpdk l2fwd如何初始化每个逻辑核的port/queue的

linux大本营

队列 DPDK DPDK开发

linux dbus客户端和服务器示例代码

linux大本营

c++ Linux dbus

minikube 初体验环境搭建

IT蜗壳-Tango

三周年连更

linux dbus代码举例

linux大本营

Linux C++

扎最深的寨,打最持久的仗——一知智能AI商业化攻略访谈录

B Impact

CefSharp自定义缓存实现

沙漠尽头的狼

打工人逃不开「单人单岗」

Java 架构 程序人生 职场

openbmc 中如何使用D-bus

linux大本营

dbus openBMC

京韵、京城、京味:从一台服务器看数字北京

脑极体

算力

React Native 优先的多端统一化方案_语言 & 开发_Qunar技术沙龙_InfoQ精选文章