本文介绍了云音乐引入 React Native 0.33 版本历史,RN 自动部署平台和离线包服务平台实现,0.33 版本如何升级到 0.6 版本,业界首个 RN codemod 框架推出,三端方案落地,基础设施完善,RN 现状及将来规划。前端
本文做者:章伟东(网易云音乐大前端团队)node
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
虽然优化结果喜人,可是存在几个问题:
此时放在团队面前有 3 条路:
通过激烈讨论和痛苦抉择,团队决定向更高目标发起冲击,不知足于只完成到达率目标,而是要重建整个 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 部署平台会自动处理兼容性、打包、上传和发布工做,支持多环境,一键部署完成整个流程。
从上图能够看出 JSBundle 请求是整个流程中性能瓶颈。 若是把加载 JSBundle 这个环节提早(在 App 初始化时触发) , 后续打开 RN 应用, App 会直接从本地加载资源包,极大提升用户体验和性能。
基于上述缘由,咱们设计了 RN 离线包服务平台来负责 JSBundle 下发。 离线包服务和构建部署紧密相关,咱们将 2 个平台打通,在构建部署阶段自动生成离线包,减小开发人员部署工做。
下面是 RN 自动部署平台和离线包服务平台整个流程图
主要流程以下:
升级工做主要分两块: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 个组件:Listview
和 navigator-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
其主流程以下:
整个框架一共处理了 12 条转译规则
此框架完成后,一天以内完成了全部 RN 应用升级,不只保证准确性,减小人力成本和时间,还为从此升级提供了扩展。
当上面升级完成以后,团队开始投入 3 端方案的研究,经调查主要有 3 种方式:直接转换、桥接模式、底层构建。
由于 RN 与 React 只是渲染层面语法的不一样,因此若是可以将 RN 的语法直接翻译为 React 语法,那么就能够将 RN 跑在浏览器上。
好比将 RN 的 View
转为 React 的 div
,RN 的点击事件 onPress
转为 React 的 onClick
等。
这种方案的缺点在于:
View
,Text
,Image
基础组件很是多。View
里面有一个onStartShouldSetResponder
方法,React 里面找不到对应事件。对于 RN 应用,先找到一个支持 forweb 的 三方框架,而后把 RN DSL 转为第三方框架的 DSL 达到最终目的。
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-template
。rn-template
内置了 android,ios 和 web 开发容器及一些经常使用工程配置,集合了rn-util
(处理请求,环境判断,通用协议)和三端组件库。
通过上述努力,收银台在 RN 0.6 版本上完成了重构,到达率从以前 H5(已优化) 89% 升至 99%。
随着 RN 版本的提高,基础建设完善,愈来愈多大前端开发人员在新项目中采用了 RN 技术栈。
目前已经上线了 10 多个 RN 应用,例如:
目前 RN 技术已经成为大前端重点发展方向,有专人专项来负责此事,后续的具体规划围绕性能、效率、监控三大方向展开,目标在这块打形成业界第一梯队。
如今有多个专项正在推动中
这个专项的主要目的是打通 RN bridge 和 JS bridge,可让一套数据通讯机制同时支持 RN 和 web。
以前的 bridge 主要有 2 个问题:
因此,针对上面状况,大前端这边统一了两端 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 应用在大部分主流机型上性能表现良好,可是在部分 Android 低端机出现卡顿现象。为了解决这个问题,启动拆包专项,主要分红 2 部分。
除了上述专项以外还有 RN 大盘监控、RN 资源包定向下发、文档规范等多个专项正在如火如荼的展开。
写到这里,你是否好奇云音乐 App 里面 RN 的真实体验如何,若是感兴趣,请将云音乐 App 版本升级至最新进行体验。
网易技术热爱者队伍持续招募队友中!咱们一直在招人,若是你刚好准备换工做,又刚好喜欢云音乐,那就发送简历至grp.music-fe@corp.netease.com!加入咱们吧!