学习 携程开源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

你可能感兴趣的:(学习 携程开源RN开发框架 - CRN)