学习 携程开源RN开发框架 - CRN

一、RN在公司的使用情况

App 2018年中正式引入React Native,评论列表、评论详情等页面都是RN开发的。

React Native优势

它对比原生开发更为灵活,对比H5体验更为高效。
替代传统的WebView,打开效率更高,和原生之间的交互更方便。
多个版本迭代后的今天,它已经拥有了丰富第三方插件支持。
React Native解决不了的,可以通过各位熟悉的原生来解决,互补益彰。
更方便的热更新。

React Native缺点

尽管是跨平台,但是不同平台Api的特性与显示并不一定一致。
相对增大了app的体积。
调试’相对‘麻烦。
Android上的兼容性问题。
首屏加载慢。

二、CRN框架

携程基于React Native框架优化,定制成适合业务的跨平台开发框架 - CRN,提供从开发、发布、运维的全生命周期支持。


  • 开发框架,主要是提供在开发阶段的支持。包括工具&文档、组件和解决方案、跨平台打通。 工具主要包括CLI和Packer,文档包括API文档和设计文档。

  • 性能优化,主要是为了解决首屏渲染的性能问题和RN框架的稳定性问题。为了解决首屏渲染性能问题,我们(携程)先后开发了框架拆分和预加载、业务按需加载、业务预加载和渐进式渲染方案。

2.1 开发框架

以下是crn-cli脚手架,对RN原始的CLI进行二次包装,提供从工程创建,服务启动,在已集成框架的App运行RN代码等常用功能,方便开发人员快速上手。

Commands:
   init                   建立并初始化CRN工程,可指定appId,默认为携程App
   start                  启动CRN服务,默认端口5389   
   run-ios                运行指定appId的IOS App
   run-android            运行指定appId的Android App
   run-patch              执行patch,替换CRN修改过的lib文件
   cli-update             更新cli版本
   example                创建CRN组件和API调用Demo工程
   aux                    增强型功能入口:log Server、本地打包、上传开发包等Options:
   -h, --help             显示命令帮助
   -v, --version          显示版本

2.2 对官方RN的修改

CRN是基于ReactNative定制的,我们对其Runtime、CLI工具代码,都有调整。 主要改动点包括:

  • 支持拆分之后的包运行, 针对CRN打包格式的nativeRequire实现
  • 增强稳定性,主要是Android平台, 大量的异常处理和保护
  • 增强稳定性,主要是Android平台, 大量的异常处理和保护

三、CRN性能优化

  • 页面加载流程


以上是一个RN页面加载的全流程,首选是Native容器的创建,接着是下载安装最新包(如果有的话),之后开始CRN框架(包含Native和JS组件)加载,框架加载完成之后,加载业务代码,计算页面虚拟dom,通知Native进行页面首次渲染,如果有网络请求,请求完成之后,再次渲染。

灰色部分是可选的,真实RN页面的渲染性能包含4、5、6三部分,针对这三部分,我们提供了不同的性能优化方案。

  • CRN框架加载:框架和业务代码拆分、框架代码预加载、JSC执行引擎缓存
  • 业务代码加载:业务代码按需加载、业务代码预加载
  • 业务页面渲染:渐进式渲染、骨架图预渲染

3.1 CRN框架加载的优化

可见CRN优化后的页面首屏加载时间与优化前RN官方的方式相比在iOS上减少了50%左右,Android上减少了60%左右,优化效果明显。

3.2 业务代码加载优化

按需加载:是进入业务模块时候,只加载对应页面的代码
预加载: 是尚未进入业务模块前,即把需要进入业务页面的代码在后台加载执行掉

3.2.1 业务代码按需加载

LazyRequire按需加载方案
先来看一段我们初始化页面路由表的代码

import PageA from ("pages/PageA");
import PageB from ("pages/PageB");
import PageC from ("pages/PageC");
import PageD from ("pages/PageD");

//设置页面路由表
let pageList = [PageA, PageB, PageC, PageD];
App.startApp(pageList);

早期业务简单,页面数量少,上面的优化方案已经可以是RN基本达到native的体验,但是随着业务越来越复杂(当时有业务bundle,包含70多个Page js代码uglify之后达到3MB),首屏加载慢的问题又出来,为此我们实现一种懒加载的方案,进入业务时候,只加载当前需要显示的Page的代码, 对业务的使用非常简单,下面是我们懒加载的页面路由代码写法。

const PageA = lazyRequire("pages/PageA");
const PageB = lazyRequire("pages/PageB");
const PageC = lazyRequire("pages/PageC");
const PageD = lazyRequire("pages/PageD");
//设置页面路由表
let pageList = [PageA, PageB, PageC, PageD];
App.startApp(pageList);

对业务开发来说,切换成本非常低,只需要使用lazyRequire函数替代import指令。

//LazyRequire函数定义,返回lazyModule对象
LazyModule lazyRequire(path)

LazyModule = {
    load(); //代码真正执行的点,返回执行结果
}

细心的同学可能发现这里有个问题,lazyRequire函数传入的文件相对路径,打包之后,还是相对路径,而打包完成之后,每个业务js模块都被打成模块ID.js文件,这会导致运行时查找不到这些业务页面的模块。是的,在打包过程中,需要开发一个babel插件,将lazyRequire函数例的文件路径,转换成模块ID,实现方式和import 的babel插件基本一致。

随着业务代码增加,进入首屏需要加载(require)的代码会增加,前面分析过,require会导致JS代码的执行,是耗时的操作,最终导致首屏变慢。所以,我们就想,进入业务的时候,只加载第一个Page相关的代码,其他页面的,路由跳转过去的时候再加载。

3.3 业务页面渲染

我们发现,随着页面复杂度增加,渲染耗时逐渐增加,这也可以理解,要完成页面渲染,需要计算vitrual dom的diff,传输数据给native,如果数据传输有延迟,就会出现掉帧,为了让页面尽可能快的显示,我们需要简化首次渲染。
渐进式渲染
先渲染header部分,setTimeout去渲染其余部分,如果是listview/scrollview,先渲染屏幕可视区域,在滑动时候,再渲染其他区域。下面一个demo视频,我们看下。
https://v.qq.com/x/page/u08125tcr7w.html

四、打包

分平台打包

目的是抹平组件的平台差异,解决资源加载路径不一致的问题。很长一段时间,我们iOS/Android的业务代码,只打一次包,以iOS平台打包。因为涉及到Native代码的新组建的引入,都是由框架团队控制,所以一直以来都没出什么问题。直到公司内部独立App,他们引入的第三方组件iOS/Android有差异,导致发布之后在Android上运行有问题。

分平台打包之后,先打包iOS,再打包Android,将差异代码存储在js-diff目录,加载时,Andorid先在js-diff中查找模块,查找得到直接使用,如果查找不到,再在默认的js-modules文件夹中查找。iOS则只在js-modules文件夹中进行模块查找。

五、如何接入

为了方便接入,首先安装crn-cli, 执行 npm install -g crn-cli 即可。

现有app接入

  • JS代码部分
    只需在现有JS入口模块文件如index.js中添加一行模块导出代码即可,示例如下:

//index.js
AppRegistry.registerComponent(appName, () => App);
module.exports = App; //添加此行代码,导出入口模块即可

  • Native Runtime接入
    1.将iOS/Android目录下的Runtime代码替换RN官方代码,具体参考项目README文档
    2.启动逻辑中添加webapp目录代码物拷贝到工作目录,可参考CRNDemo工程源码
    1. 启动时调用框架预加载代码

使用crn-cli pack命令打包,并将打包产物拷贝到Native工程的webapp目录

六、总结

CRN框架对原生RN的大量底层改造优化,解决了性能和稳定性两大核心问题,从落地效果来看,其性能可以做到和Naitve基本一致水平,而开发成本却大幅降低。

CRN框架已在业务团队中广泛使用,为业务的快速迭代提供了强有力支持。对于规模化业务开发团队,使用RN作为跨平台开发的解决方案,是切实可行的选择。

跨平台方案React Native和flutter对比

https://juejin.im/post/5c469f56e51d456e4138f911
https://juejin.im/post/5cd4059851882547a572f540

参考:
https://mp.weixin.qq.com/s/Z1GUJW3qBqDGH1jnGt5qAg
https://mp.weixin.qq.com/s/yPgyqrggtYbrg4htEaw_wg

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341