乐趣区

关于react-native:云音乐-React-Native-体系建设与发展

本文作者:章伟东

0.33 历史

17 年 3 月份,为了解决商城性能和用户体验问题,云音乐技术团队组建了一只 4 人 ReactNative 开发小分队:我负责 RN 前端开发,安卓和 iOS 两位开发负责在云音乐 App 外面嵌入 RN Native SDK,还有一位 Java 开发来负责部署平台工作。

商城 RN 利用上线后,其余团队示意有趣味尝试,但过后 RN 我的项目开发没有脚手架,我的项目创立通过原始拷贝进行,短少 forweb 反对,RN 预加载只接入了 iOS 一端。

种种原因,导致 RN 开发效率低下,音乐人业务本来有趣味用 RN 来开发新利用,开发到一半改成了 H5。

从 17 年 3 月份到 19 年 9 月份,RN 版本始终为 0.33,外围开发团队人员散失一半,部署平台无人保护,我的项目开发短少脚手架,短少 forweb 反对,一共上线 RN 利用为 2.5 个(商城、音乐人、三元音箱)。

搅动历史

工夫滚滚向前,新技术层出不穷。2 年半的工夫对于前端倒退来说,恍如隔世。如果不出任何意外,RN 技术就会躺在历史的尘埃里,无人问津。这种难堪的场面,直到会员收银台达到率优化我的项目才被突破。

会员收银台页面即下图,是云音乐会员购买页面,重要性显而易见。这个页面最开始是一个 React 服务端渲染开发的 H5 页面。

为了能让用户更加顺利购买会员,进步用户体验和达到率,整个技术团队采纳 web 通用优化技术联合云音乐本身技术设施,花了一个月对这个 H5 页面进行优化,将达到率从 72% 进步到 89%,进步了 17 个百分点。与竞品比拟如下(单位是秒)。

达到率计算公式 = 收银台可视埋点 / 客户端点击埋点

尽管优化后果喜人,然而存在几个问题:

  1. 达到率指标未实现。当初技术团队定的是至多 90% 以上,差了一个百分点。
  2. ROI 太差。H5 优化投入了前后端开发泛滥人力,花了将近一个月。如果再去优化其余页面,目前计划自动化水平低,仍需大量人工操作。
  3. 0.33 RN 达到率为 93%。咱们统计了商城 RN 版本的达到率,未做任何优化,轻松破 90。

此时放在团队背后有 3 条路:

  1. 在 H5 页面上投入更多资源优化,冲破 90% 实现工作。但这种计划消耗大量的人力物力,对优化其余页面用途不大,属于一锤子买卖。
  2. 在 RN 0.33 版本上重置收银台页面。这样尽管能达到目标,然而 RN 基础设施依然停留在 3 年前。
  3. 将 RN 根底建设补齐,降级到最新 0.6 版本,实现三端计划,构建残缺的 RN 开发体系。在此基础上,基于 0.6 版本重置收银台,借助这个我的项目将 RN 整个技术栈更新换代。这种计划尽管收益大,但时间跨度长、艰难大、复杂性高。

通过强烈探讨和苦楚抉择,团队决定向更高指标发动冲击,不满足于只实现达到率指标,而是要重建整个 RN 技术体系,为当前的开发铺平道路,一劳永逸解决整个前端开发的性能和体验问题。

主动部署

旧部署平台

原有 RN 部署平台没有实现主动部署,公布一个 RN 利用须要做以下事件

执行兼容性脚本

为了反对低版本如 iOS8,须要手动批改本地 node_modules 外面相干源码。

    sed -i -e 's/function normalizePrefix(moduleName: string)/const normalizePrefix = function(moduleName: string)/g' ./node_modules/react-native/Libraries/BatchedBridge/BatchedBridgedModules/NativeModules.js

    sed -i -e 's/function normalizePrefix(moduleName: string)/const normalizePrefix = function(moduleName: string)/g' ./node_modules/react-native/Libraries/Utilities/UIManager.js

    sed -i -e 's/function handleError(e, isFatal)/var handleError = function(e, isFatal)/g' ./node_modules/react-native/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js
执行打包脚本

本地执行release.test.sh(测试)和release.sh(线上)。release 脚本别离调用 iOS 和 Android 打包脚本,而后打出对应的 bundle。

因为两端 bundle 用同一个名字,所以很容易呈现传错状况,每次上传都小心翼翼。

上传公布平台

这里须要填写相干内容,而后点击公布。

能够看到下面三步有本地净化危险,操作繁琐,容易脱漏步骤和填错。

主动部署流程

针对下面手动部署缺点,咱们从新梳理和设计了整个主动部署流程

git 克隆 -> 依赖装置 -> 主动脚本执行 -> 压缩 -> 上传文件服务器 -> 保留版本信息 -> 公布

而后用 Node 取代 Java 开发了新 RN 部署平台

新 RN 部署平台会主动解决兼容性、打包、上传和公布工作,反对多环境,一键部署实现整个流程。

双端预加载

RN 加载流程

  • APP 先启动 RN 容器,RN 容器从服务端申请 JSBundle,而后进行初步渲染。
  • RN 页面初始化实现后,向服务器发申请拿动态数据,实现剩下渲染逻辑

从上图能够看出 JSBundle 申请是整个流程中性能瓶颈。
如果把加载 JSBundle 这个环节提前(在 App 初始化时触发),后续关上 RN 利用,App 会间接从本地加载资源包,极大进步用户体验和性能。

RN 离线包平台

基于上述起因,咱们设计了 RN 离线包服务平台来负责 JSBundle 下发。离线包服务和构建部署严密相干,咱们将 2 个平台买通,在构建部署阶段主动生成离线包,缩小开发人员部署工作。

上面是 RN 主动部署平台和离线包服务平台整个流程图

次要流程如下:

  1. RN 主动部署平台先构建出全量包,传到 CDN 上,而后告诉离线包服务平台
  2. 离线包服务平台收到全量包信息,用 diff 算法算出差量包,存储相干的信息,公布差量包。
  3. APP 启动的时候拜访离线包服务,依据返回的信息来读取本地缓存还是去近程取对应的全量包或差量包。

0.33 降级 0.6

降级工作次要分两块:RN Native SDK 降级 + RN 利用降级。

RN Native SDK 指的是集成在云音乐 App 外面 RN 相干原生代码(iOS 和安卓源码)。因为 0.33 版本和 0.6 版本无奈同时兼容,所以咱们对于老版本采取了只保护,不降级的策略。

RN 利用指的是例如商城、音箱这种业务利用,也能够等同于 JSBundle。利用降级必须赶在 SDK 降级之前实现,不然会呈现 0.6 SDK 加载 0.3 利用的状况,导致 App 解体。所以,所有利用必须同时实现降级工作

降级面临问题

依赖问题

RN 0.3 应用的是 React 15.3 版本,0.6 应用的是 16.8。除了 React 的依赖之外,还有其余依赖须要降级,咱们依据官网提供 版本差别比拟 创立了一个脚手架,读取 package.json 外面信息,一一比对,而后批改为对应版本。

废除组件

RN0.6 版本移除了 2 个组件:Listviewnavigator-ios

对于这种状况,如果咱们用新组件比方 FlatList 重写,不仅须要了解原来业务逻辑,还要批改源码,从新测试。所以针对这种状况,团队采取措施是:不改变现有代码,从旧版本抽取对应组件。
最终,咱们公布了@music/rn-deprecated-navigator-ios@music/rn-deprecated-listview

语法兼容

RN 语法在 0.6 和 0.33 上不仅写法不同,也不向下兼容。导致的后果就是 0.33 的 JSBundle 跑在 0.6 的 RN Native SDK 上会间接解体,上面以背景图举例说明。

在 0.33 中为了实现背景图,是用 Image 蕴含一个View, 而到了 0.6 外面改成了ImageBackground,属性也不同。

除了背景图的语法须要批改之外,还有多少语法须要兼容批改咱们不得而知。面对这种范畴不分明,改变工夫又十分缓和的状况,如果应用人工形式不仅效率低下进度也不可控。因而,咱们采纳了自动化的解决形式,推出了业界首个 RN codemod 框架 mrn-codemod

其主流程如下:

  1. 利用框架先读取 0.33 源码
  2. 将 0.33 源码 转为 AST 树。
  3. 对 0.33 AST 树进行对应操作,转化成 0.6 的 AST 树。
  4. 把 0.6 的 AST 树从新生成源码。

整个框架一共解决了 12 条转译规定

此框架实现后,一天之内实现了所有 RN 利用降级,不仅保障准确性,缩小人力老本和工夫,还为今后降级提供了扩大。

3 端计划

当下面降级实现之后,团队开始投入 3 端计划的钻研,经考察次要有 3 种形式:间接转换、桥接模式、底层构建。

间接转换

因为 RN 与 React 只是渲染层面语法的不同,所以如果可能将 RN 的语法间接翻译为 React 语法,那么就能够将 RN 跑在浏览器上。

比方将 RN 的 View 转为 React 的 div,RN 的点击事件 onPress 转为 React 的 onClick 等。

这种计划的毛病在于:

  1. 工作量太大。RN 外面的 ViewTextImage 根底组件十分多。
  2. 无奈做到一一对应。比方 View 外面有一个 onStartShouldSetResponder 办法,React 外面找不到对应事件。

桥接模式

对于 RN 利用,先找到一个反对 forweb 的 三方框架,而后把 RN DSL 转为第三方框架的 DSL 达到最终目标。

这方面比拟有代表性的就是 Taro 和 ReactXP。

Taro 依据 RN 标准本人实现了一套 DSL,对函数和事件做了自定义。

ReactXP 三端反对十分良好,然而组件非常少,也只好放弃。

底层构建

依据 RN 元素和组件定义,从最底层开始用 WEB 相干个性来实现整套 RN API,这个就是 react-native-web。这种计划也是目前业界支流模式。

咱们对这个库进行了封装和扩大,增加了不反对的组件,修复了一些 bug,造成 @music/react-native-web-suffix

新开发流程

咱们在三端计划的根底上开发了 rn-cli 脚手架,rn-util常用工具库,rn-template工程初始化模板等配套工具,造成了一整套 RN 开发的基础设施,目前新开发流程如下

rn-cli脚手架初始化的时候会调用 rn-templatern-template 内置了 android,ios 和 web 开发容器及一些罕用工程配置,汇合了rn-util(解决申请,环境判断,通用协定)和三端组件库。

收银台 RN 重构后果

通过上述致力,收银台在 RN 0.6 版本上实现了重构,达到率从之前 H5(已优化)89% 升至 99%。

现状

随着 RN 版本的晋升,根底建设欠缺,越来越多大前端开发人员在新我的项目中采纳了 RN 技术栈。

目前曾经上线了 10 多个 RN 利用,例如:

将来布局

目前 RN 技术曾经成为大前端重点倒退方向,有专人专项来负责此事,后续的具体布局围绕 性能 效率 监控 三大方向开展,指标在这块打造成业界第一梯队。

当初有多个专项正在推动中

Native RPC

这个专项的次要目标是买通 RN bridge 和 JS bridge,能够让一套数据通信机制同时反对 RN 和 web。

之前的 bridge 次要有 2 个问题:

  1. 用法不统一。须要写 2 套语法别离反对 RN 和 web。
  2. 反对不统一。有的协定 web 有 RN 没有,反之同样。

所以,针对下面状况,大前端这边对立了两端 API,重构了底层协定来反对下面的性能,上面举一个例子。

// 查看 net.nefetch 是否反对,mnb.checkSupport({
    module: 'net',
    method: 'nefetch'
}).then(res => {})

/* 手动增加办法 */
mnb.addMethod({
    schema: 'page.info',
    name: 'getPageInfo'
});

/* 增加之后即可调用 */
mnb.getPageInfo().then((result) => {// ...}).catch((e) => {// ...});

RN 和 web 两端都是对立写法,开发人员再也不必放心兼容性问题。

RN 拆包

RN 利用在大部分支流机型上性能体现良好,然而在局部 Android 低端机呈现卡顿景象。为了解决这个问题,启动拆包专项,次要分成 2 局部。

  1. 拆包。将当初的残缺 JSBundle 拆成根底包和业务包,别离载入。
  2. 容器预加载。在 App 启动的时候就预热 RN 容器,这样能够大幅度缩小容器启动工夫,进步载入速度。

其余

除了上述专项之外还有 RN 大盘监控、RN 资源包定向下发、文档标准等多个专项正在热火朝天的开展。

结束语

写到这里,你是否好奇云音乐 App 外面 RN 的实在体验如何,如果感兴趣,请将云音乐 App 版本升级至最新进行体验。

本文公布自 网易云音乐大前端团队,文章未经受权禁止任何模式的转载。咱们长年招收前端、iOS、Android,如果你筹备换工作,又恰好喜爱云音乐,那就退出咱们 grp.music-fe(at)corp.netease.com!

退出移动版