网易云音乐React Native体系建设与发展

header.png

本文介绍了云音乐引入 React Native 0.33 版本历史,RN 自动部署平台和离线包服务平台实现,0.33 版本如何升级到 0.6 版本,业界首个 RN codemod 框架推出,三端方案落地,基础设施完善,RN 现状及将来规划。前端

本文做者:章伟东(网易云音乐大前端团队)node

0.33 历史

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

商城 RN 应用上线后,其余团队表示有兴趣尝试,但当时 RN 项目开发没有脚手架,项目建立经过原始拷贝进行,缺乏 forweb 支持,RN 预加载只接入了 iOS 一端。android

种种缘由,致使 RN 开发效率低下,音乐人业务本来有兴趣用 RN 来开发新应用,开发到一半改为了 H5。ios

从 17 年 3 月份到 19 年 9 月份,RN 版本始终为 0.33,核心开发团队人员流失一半,部署平台无人维护,项目开发缺乏脚手架,缺乏 forweb 支持,一共上线 RN 应用为 2.5 个(商城、音乐人、三元音箱)。git

搅动历史

时间滚滚向前,新技术层出不穷。2 年半的时间对于前端发展来讲,恍如隔世。 若是不出任何意外,RN 技术就会躺在历史的尘埃里,无人问津。这种尴尬的局面,直到会员收银台到达率优化项目才被打破。github

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

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

竞品比较

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

虽然优化结果喜人,可是存在几个问题:

  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 部署平台会自动处理兼容性、打包、上传和发布工做,支持多环境,一键部署完成整个流程。

双端预加载

RN 加载流程

RN 应用加载流程

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

从上图能够看出 JSBundle 请求是整个流程中性能瓶颈。 若是把加载 JSBundle 这个环节提早(在 App 初始化时触发) , 后续打开 RN 应用, App 会直接从本地加载资源包,极大提升用户体验和性能。

RN 离线包平台

基于上述缘由,咱们设计了 RN 离线包服务平台来负责 JSBundle 下发。 离线包服务和构建部署紧密相关,咱们将 2 个平台打通,在构建部署阶段自动生成离线包,减小开发人员部署工做。

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

2 个平台

主要流程以下:

  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 条转译规则

mrn-codemod

此框架完成后,一天以内完成了全部 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 达到最终目的。

这方面比较有表明性的就是 TaroReactXP

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 版本升级至最新进行体验。

网易技术热爱者队伍持续招募队友中!咱们一直在招人,若是你刚好准备换工做,又刚好喜欢云音乐,那就发送简历至grp.music-fe@corp.netease.com!加入咱们吧!

相关文章
相关标签/搜索