东亚银行、岚图汽车带你解锁 AIGC 时代的数字化人才培养各赛道新模式! 了解详情
写点什么

去哪儿网 ReactNative 跨小程序多端方案介绍

  • 2021-03-08
  • 本文字数:5214 字

    阅读完需:约 17 分钟

去哪儿网 ReactNative 跨小程序多端方案介绍

1 前言

qrn-remax-unir 是由去哪儿网前端技术团队实现的一套将 RN 适配到小程序端的跨端组件,通过该组件库可快速方便的将 RN 源代码直接运行到小程序端。方案参考了 react-native-web 的适配方案,使用 remax 框架来实现适配组件库并达到适配多小程序的目的。和 react-native-web 一样,它对 RN 源代码侵入度低,并且调试和替换组件相当方便。


方案来自于社区,我们只是合理的应用用来解决我们遇到的业务问题,最终开发了这套组件库,也希望开源出来跟大家分享和共同探讨多端开发之路。


下面我们简单介绍下 RN 到小程序的适配方案和适配组件库的点滴。

2 多端方案回顾

qrn-remax-unir 组件库是整个去哪儿网跨端方案的一个部分,通过整套方案可实现将 RN 代码直接运行到 H5 和小程序,达到跨 IOS,ADR,H5,小程序的目标。整体多端方案的思路是:


  1. 使用 react-native-web 将 RN 代码适配到 H5 上运行,这也是 RN 官方推荐和集成的方式。

  2. 在 remax 组件的基础上实现一套 RN 的组件库,借用 remax 来适配到多端(qrn-remax-unir)。

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


3 RN 相关多端方案

3.1 对 alita 的调研

在最初的调研中我们注意到 alita 可以实现将 ReactNative 转换到微信小程序运行,也对方案进行了详细的调研:


  1. alita 会在编译阶段将 JSX 语法按照代码块编译成微信 wxml 的碎片,然后用 templete 包裹;

  2. 内部实现了一个 mini-react 来对接适配 react 和小程序的生命周期。


详细原理可参考:


https://zhuanlan.zhihu.com/p/69333351


https://www.jqhtml.com/44555.html


于是跑了些 demo 对方案进行使用觉得还是跟我们的期望有些差距:


  1. 虽然期望是对 RN 代码无入侵,但还是会对 RN 源码有限制的,部分特性不能用,动画之类的要求用 alita 提供的自定义动画库;

  2. 内部使用了自定义的 mini-react 来对接 react 和小程序的生命周期,考虑后期对 react 新特性的适配进度风险;

  3. 专注于 RN 到微信小程序的转换,对其他小程序不支持。


基于以上原因我们最终没有直接使用该方案,调研过程感觉他们对 jsx 模板的处理思路还是很新颖的,是很好的借鉴。

3.2 remax 简单原理

remax 可实现在小程序中用真正的 react 进行开发。remax 将自己实现成了一个 react 的渲染器,收集宿主 UI 变更指令生成页面 VNode 树,然后在小程序渲染层渲染这个树。


详细原理可参考:https://juejin.cn/post/6844904131157557262


由于基于运行时适配的方案,有以下优缺点:


  1. 适配在 react 虚拟 dom 之上,对 react 的技术栈无任何限制,redux,mobx,hooks 等都可自由使用;

  2. 模板的动态处理在渲染的时候可能效率低下,需要针对性解决;

  3. 由于没有编译处理,运行时都是平台源代码,查问题更方便;

  4. 基于 react 组件,可以很好的对小程序原生组件进行支持;

  5. 并没有提供对各个小程序的跨端组件,只对部分基础组件进行了封装和适配。

4 我们的适配方案

remax 虽然支持 remax 的语法,但是他是基于自定义组件提供的语法 DSL,提供的组件和 RN 也没有任何关系。但他们对 react 语法适配到小程序的方案还是很大胆实用的,也启发我们可以按照类似的适配方案来实现 RN 组件到小程序的适配,这样就可以将 RN 运行到小程序了。

4.1 ReactNative 到 remax 组件

调研 remax 的适配方案,虽然方案原理说起来很简单,但是真正要做这么一套东西评估了下还是非常耗时耗力的。


重新评估下来,发现 remax 其实已经可以做到 react 到小程序的适配,且提供了基础的几个跨端组件可供使用。我们直接用 remax 组件来实现 RN 的组件则可以快速的适配到小程序端,虽然可能带来部分性能损耗。


看个将 RN 的 Text 组件适配到 remax 组件的例子:


4.2 ReactNative API 的适配

对与 API 的实现要更为简单直接一些,这里请允许我将 react-native 的组件分为两类:


其一 是纯函数的 api,简单理解就是你调用了一个由框架提供的函数,得到了你想要的数据写入或返回,比如 Dimensions,这一类普通 api,qrn-remax-unir 的处理方案就是能够通过封装调用小程序提供的 api 并存在两个平台磨平参数差异可能的即可完美适配。对无法通过调用小程序 api 来实现同样效果的或小程序场景逻辑上就不存在的,qrn-remax-unir 往往通过空实现并对开发者加以告知。


其二 是调用一个有框架提供的方法,在页面上进行呈现,我们姑且叫它 api 组件(react-native 中是 api、但在小程序平台更像是组件),例如:Alert、ToastAndroid、ActionSheetIOS 等,这一类 api 的实现方案是构造展示的模板组件,暴露出方法能够间接修改组件的状态,例如 alert 就是通过获取调用方法传入的内容进行填充,当获取用户操作后修改为隐藏不显示的状态并回调组件的回调方法。

4.3 复杂组件和 API 的适配

复杂组件和第三方组件其实都是对基础元素组件的组合使用,我们从最底层的基础组件(View Text 等)完成适配,这样大部分复杂组件都可以直接适配到小程序端。如下图只要 qrn-remax-unir 的基础组件适配得足够完整就可以保证对上层 RN 业务和组件的完整适配。


5 开发过程

虽然开发思路大体清晰了,但在开发过程中还是遇到了一些比较棘手的问题。

5.1 样式处理

对于将 RN 的样式转换成小程序端能用的样式,我们一开始确定了两个方向:


  1. 编译时提取并转换

  2. 运行时动态生成


编译时提取并转换


这个方式其实是我们一开始经过讨论觉得可行的方案,具体方式就是通过编写 Webpack 以及 Babel 插件,实现在编译阶段对样式相关对 RN 代码进行抽取并生成最终的 WXSS 文件。


这个方案的好处就是:


静态编译出的文件可以更好的进行管理,对于样式转化出的问题可以直观的进行修改,相当于有一个可见的原生产物去进行调试,对于开发者显得更友好,更直观。


实现的效果理想中是这样的。


对于源代码:


const styles = StyleSheet.create({    container:{        width:100,        height:100    }}) <View style={styles.container}>    <Text>Hello World</Text></View>
复制代码


处理后生成的 styles.wxss 文件中:


.container-test {    width:100px,    height:100px}
复制代码


然而在尝试开发好了相关 babel loader 以及 webpack 插件以后,面对真实的复杂项目时,却发现代码的书写复杂情况很难穷举解决。


因为 RN 代码的样式并不总是规矩的写成:


const styles = StyleSheet.create ({    key:value})
复制代码


在项目中由于一些样式的值,例如屏幕宽高等,需要在运行时获取,于是会有一些不规则的赋值形式:


const height = DeviceInfo.isAndroid?100:120;const styles = StyleSheet.create ({   height})
复制代码


当出现这种动态获取运行时值的情况时,我们的这个策略就无法处理了,于是这个方案只能暂且作罢。


运行时动态生成


这种方案的基本实现方式其实是借鉴了 React-native-web 的做法,也许 Twitter 也尝试过编译时去生成 CSS 文件转换到 web 的方案,最后未果,才使用的运行时提取 RN 样式成 class 并赋值到 DOM 元素上的方案。当然,通过分析了 react-native-web 的源码库,我们做了一定程度的改动以适配到微信小程序的情况。具体做法如下:


以最基础组件 View 为例:


import { View , Text } from 'react-native';
```<View style={{flex:1}}> <Text>Hello World</Text></View>```
复制代码


进行完转换的 className 以及 style,会随同一些默认补全样式,传递到 Remax 的 View 组件中对于一个普调的 View 组件,我们按照 RN 正常书写习惯,对 style 赋值。我们适配层做的核心处理,就是将这些 styles 解析,并且转换和处理 Class 以及数组格式的样式,生成 classList 以及 style 变量。


const convertStyle = (styles) => {    const classList = [];    const style = {};    const convert = (value) => {        // 对于Class,直接增加进classlist        if (typeof value === 'string') {            classList.push(value)        } else {            // 对于数组,递归处理每一个元素            if (Array.isArray(value)) {                value.forEach(item => {                    convert(item)                })            } else {                // 对于一般的属性,直接转换成行内样式                value && Object.assign(style, inline(value))            }        }    }    convert(styles)    return {classList, style}}
复制代码


进行完转换的 className 以及 style,会随同一些默认补全样式,传递到 Remax 的 View 组件中。


// 默认样式supportedProps.className = "view-default " + classList.join(" ");supportedProps.style = style;supportedProps.id = this.props.id;return (<RemaxView {...supportedProps}/>)
复制代码


至此在运行时的 Dom 渲染,就会得到相应的 class 名以及行内 style 的值。



所以,对于编译时遇到的,那种 DeviceInfo 这类赋值,也会在运行时得到最终的值并添加到组件的 style 属性里,从而解决了编译时无法控制的事情。


样式小结与思考


对于样式的处理,有很多细节需要注意,例如对于不同端的单位转换问题,对于不同端一些默认样式的补全的设置等。此处重在方案的选择过程介绍,并未列出具体样式差异,还需要更多的人来贡献力量,持续投入,跟进 react-native 的版本以及 remax 的版本。而且对于 StyleSheet 的细节处理,感兴趣的同学也可以去查看 react-native-web 的源码,还是值得对跨端开发感兴趣的同学深入研究学习的。关于方案,我们也在思考是否可以更进一步提升,比如将编译和运行时结合,从而减少行内样式,目前这样直接写入行内样式还是会导致我们的 dom 元素上属性值过长,不可读,不利于开发调试。另一方面,这种过长的属性也使小程序端的性能表现受到了一定的影响。

5.2 对动画的处理

qrn-remax-unir 在实现动画方案上,采取了尽量不引入新的写法,与 react-native 原本写法尽量保持一致的思路,对 RN 代码做到零入侵。


这里准备了一个 demo,代码中的动画写法和 react-native 官方文档的写法是一致的,先看一下效果。


视频地址


在定义 react-native 的单一动画是这样写的:


Animated.timing(    this.state.opacity,    {        toValue:1, // 1        duration:3000, // 2        easing:Easing.ease // 3    }).start(); // 5
复制代码


qrn-remax-unir 将上面的代码转换成类似下面的能够在小程序运行的代码,1-5 的动画关键信息通过框架层处理成一一对应,过渡时间就是 react-native 的动画时间即 duration,opacity 就是 react-native 的 Animated 组件的动画样式 key 即 opacity、linear 就是 react-native 中的赋值 API 即 Easing(有一些复杂曲线难以适配会在小程序端做相应的降级处理,比如使用 linear),初始值和目标值直接传递过去就可以了再利用插值器将原始单位正则解析出来放如动画属性值中,产物就是这样的 transition: 3000ms opacity cubic-bezier(0.42, 0, 1, 1);


组合动画、平行动画和顺序延迟动画都是由 Animated.timing 组成动画序列,由 react-native 的 Animated 本身的处理机制处理多个动画触发和销毁。

6 组件库的使用

组件库地址:https://github.com/qunarcorp/qrn-remax-unir


组件 demo 地址:https://github.com/qunarcorp/qrn-remax-unir-demo


组件文档地址:https://qunarcorp.github.io/qrn-remax-unir/#/./


想要了解具体如何使用我们的组件库,可以下载我们的示例工程进行开发学习:传送门(https://github.com/qunarcorp/qrn-remax-unir-demo/tree/master


这个工程内集成了 RN,web,微信小程序三端的编译开发能力,并且启动十分简单,你可以基于这个项目进行跨端项目的改造。此项目开发时效果如下图(自左至右:RN->Web→微信小程序):



项目启动过程很简单,进入到项目根目录下:


1、安装依赖


yarn
复制代码


2、运行 dev 环境


yarn dev         //RN,web环境yarn dev:wx      //微信小程序,需要用小程序IDE打开./dist/wechat目录进行调试开发
复制代码


此时修改代码就可以实时热更新每一端的代码,是不是很方便!赶快开启你的跨端开发之旅吧!


如果你想了解具体搭建过程,也可以参考我们 github 网站上的介绍:传送门(https://qunarcorp.github.io/qrn-remax-unir/#/./),尝试自己去动手改造出这样一个跨端项目。


作者介绍


冯地木


去哪儿网前端技术总监


08 年开始从事前端工作,曾就职于人人网、新浪微博、爱奇艺等,12 年加入去哪儿网,主导多次前端技术架构升级,推进 Node 前后端分离,React,RN 等技术架构在业务上的落地应用,擅长各种前端性能优化和工程化实践,目前负责推进 Qunar 的多端统一化升级方案。


孟晨


2019 年加入去哪儿网,擅长多种前后端语言,主要负责 Node 工程化,规范化工作,现阶段工作重心为多端统一框架的开发与推广。


王佳峰


2019 年加入去哪儿网,主要负责公共产品业务向 RN 技术栈迁移以及主流程 RN 工程向三端工程迁移。主要涉及 React-Native、Touch、小程序业务开发和多端融合框架等基础架构的研发。


本文转载自公众号 Qunar 技术沙龙(ID:QunarTL)。


原文链接


去哪儿网 ReactNative 跨小程序多端方案介绍

2021-03-08 18:427136

评论 3 条评论

发布
用户头像
2021-03-08 15:18
回复
??
2021-03-08 15:39
回复
里面嵌套了HTML出了些问题,已经修复。
2021-03-08 18:47
回复
没有更多了
发现更多内容

这一篇 K8S(Kubernetes)我觉得可以了解一下!!!

牧小农

Docker 容器 kubenetes

Redis核心原理与实践--Redis启动过程源码分析

binecy

redis 源码阅读

企业级开发工具,J2PaaS低代码平台核心能力解析!

J2PaaS低代码平台

软件开发 低代码 低代码开发 低代码平台

代码简洁之道:一行Python代码解决问题是时尚还是玄学

博文视点Broadview

网络生病了怎么办?看华为云网络测量如何“悬丝诊脉”

华为云开发者联盟

华为云 云网络 网络故障 网络测量 SDN架构

以开发之名 | 美好出行体验,“管家”一站实现

最新动态

短短 146 天就成为 Apache APISIX Committer,我是怎么做到的?

API7.ai 技术团队

成长笔记 API网关 社区 Apache APISIX

敏捷QA需要编写测试用例吗?

BY林子

测试用例 敏捷测试

只需2步,教你在Vue中设置登录验证拦截

华为云开发者联盟

Vue 浏览器 Token pringboot 登录验证

Linux指令日志分析(一)

正向成长

日志分析

Vue进阶(幺伍叁):Vue-highlight 实现代码高亮

No Silver Bullet

Vue highlight.js 10月月更

学习总结(第一周)

Geek_1d37ea

架构实战营

游戏数字资产复用——有哪些是你需要知道的?

龙智—DevSecOps解决方案

游戏开发 游戏引擎 perforce

如何赋能APaaS平台应用管理员

明道云

实时大数据Flink知识结构(超全整理,附知识脑图)

大象灵感

大数据 flink spark 实时数仓 流处理

IOS技术分享| ARCall视频通话重构

anyRTC开发者

ios 音视频 语音通话 视频通话 视频呼叫

【IT运维小知识】安全组是什么意思?

行云管家

网络安全 安全 数据安全 安全组 IT运维

网易云音乐音视频算法的 Serverless 探索之路

阿里巴巴中间件

云计算 阿里云 Serverless 云原生 中间件

OKR与影响地图,别再傻傻分不清

华为云开发者联盟

OKR 敏捷 影响地图 规划 目标

数仓无损压缩算法:gzip算法

华为云开发者联盟

算法 deflate 无损 gzip 压缩数据

引导行业发展!旺链科技加入“可信区块链推进计划”

旺链科技

区块链 数字经济 产业区块链

官方线索 | 阿里云1024程序员创造营

穿过生命散发芬芳

1024我在现场

华为云网络覆盖全球2500+站点,打造高品质、低成本接入体验

华为云开发者联盟

RTC 华为云 媒体服务 LiveVideoStack

业内首款云原生技术中台产品云原生 Stack 来了!

阿里巴巴云原生

阿里云 云原生 技术中台

阿里二面:为什么要分库分表?

Java MySQL 数据库 架构 面试

医药研发企业数据中台的选择

鲸品堂

产业互联网

模块一作业

Geek_1d37ea

网易云信被纳入 Gartner 2021年《CPaaS 市场指南》研究报告

网易云信

音视频 云通信 Gartner

Flux架构思想在度咔App中的实践

百度Geek说

百度 架构 后端 短视频 Flux

2021年10月国产数据库流行度排行解读 浅谈基础软件发展之道

墨天轮

数据库 华为 opengauss Gauss DB 国产数据库

iOS 崩溃日志在线符号化实践

百度Geek说

去哪儿网 ReactNative 跨小程序多端方案介绍_大前端_Qunar技术沙龙_InfoQ精选文章