美文网首页CRN
学习 携程开源RN开发框架 - CRN

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

作者: 大大大大大大的大大 | 来源:发表于2019-05-16 12:04 被阅读0次

    一、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

        本文链接:https://www.haomeiwen.com/subject/nyvbaqtx.html