CodePush 是微软提供的 RN 模块热更新服务,目前已整合到 App Center,CodePush 的 RN 源码已开源在 GitHub。

CodePush 功能:

  1. 支持更新 js, html, css, image
  2. 更新包体积小(不包含 RN 框架)
  3. 可以创建多个部署,区分测试环境和发布环境
  4. 有发布历史,支持回滚,不能删除发布历史
  5. 支持管理合作者

安装 CodePush

测试环境: Mac, Xcode 9.3

react-native-cli: 2.0.1
react-native: 0.55.3
code-push: 2.0.5

在命令行,进入项目根目录,执行

npm install --save react-native-code-push

登录/注册

Code Push 大部分操作都在命令行执行,部分操作也可以在网页后台操作,看个人习惯。

如果还没有帐号,可以直接在网页端注册,或者在命令行执行:

code-push register

会打开一个网页,注册完成后会生成一个 token,复制到命令行完成客户端的登录。

如果已经有帐号,在命令行执行:

code-push login

在打开的网页端登录帐号,登录成功后会生成一个 token,复制到命令行完成客户端的登录。

帐号相关命令:

code-push login 登陆
code-push loout 注销
code-push access-key ls 查看登陆token
code-push access-key rm <accessKye> 删除某个 access-key

应用管理

下面的命令用于创建应用,iOS 和 Android 需要分别创建。

code-push app add <MyApp-iOS> ios react-native
code-push app add <MyApp-Android> android react-native

查看用户可管理的 app:

code-push app ls

建议在网页后台管理 app,比较直观,网页版支持创建、删除、移交。

合作者

Code Push 支持设置合作者,可以给一个 app 添加若干合作者,也可以创建一个组织,该组织下的人员可以共同维护组织下的多个 app,公司团队建议使用组织管理。

合作者、组织管理建议在后台操作,后台简单易用,熟悉一下就可以使用了,这里不做介绍。

部署环境

Code Push 支持创建多个部署环境,这样可以支持区分 iOS、Android 平台,也可以区分生产环境和测试环境。

刚创建的 app,默认包含 Production/Staging 2个部署环境,其中 Production 用于正式环境,Staging 用于测试过度,测试完成后再从 Staging 推送(Promote) 到 Production。

可根据需要自行管理部署环境,简单应用不建议修改,使用默认的即可。

部署APP相关命令

code-push deployment add <appName> 增加部署环境
code-push deployment rename <appName> 重命名
code-push deployment rm <appName> 删除部署环境
code-push deployment ls <appName> 列出应用的部署情况
code-push deployment ls <appName> -k 查看部署的key
code-push deployment history <appName> <deploymentNmae> 查看历史版本(Production 或者 Staging)

集成 CodePush

iOS

模块集成

iOS 项目引入 CodePush 有三种方案:

  1. rnpm - React Native Package Manager (RNPM):推荐使用

    RN 版本在 v0.27 之后 React Native CLI 自带rnpm link,在项目根目录执行命令:

    react-native link react-native-code-push

    脚本运行时要求输入 CodePush deployment key,用于发布模块到指定部署和从指定部署更新模块,这里直接按回车键不填,后文会介绍怎么配置多个 CodePush deployment key

  2. CocoaPods: 如果 RN 是通过 CocoaPods 引入的,可以使用该方案,如果是使用 RN 初始化的项目可能会有冲突。

  3. 手动

打开 AppDelegate.m,确认 jsCodeLocation 设置正确,如果使用方案1,下面的代码应该会自动生成:

#import <CodePush/CodePush.h>

#ifdef DEBUG
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
jsCodeLocation = [CodePush bundleURL];
#endif

注意这里 DEBUG 版本使用的是原配置,确保开发时调试和热更新功能正常,RELEASE 版本使用 [CodePush bundleURL],支持热更新 RN 模块。

完成 CodePush 链接后,尝试编译工程,顺利的话能正常编译通过。

如有疑问,见官方教程

多部署环境配置

获取 Deployment Key

code-push deployment ls <appName> -k

参照官方教程配置好 Release 和 Staging 这两个 Build Config。这里配置的 Deployment Key 可以区分不同的部署环境,其中 Staging 用于测试,Release 用于发布到市场。

注意:切换 Build Config 时,需要手动删除之前生成的 app 文件,避免缓存造成的影响。要确认生成的安装包 Deployment Key 是否正确,可以查看 app/Info.plist 中的 CodePushDeploymentKey

RN 模块更新

模块更新在 js 中触发,可以用 CodePush 封装的默认流程,也支持手动调用检查更新的方法,这里介绍一下简单的用法,如需要手动触发检查更新,参考官方文档

用 CodePush 封装 js 入口 app:

import codePush from "react-native-code-push";

class MyApp extends Component<Props> {

}

let codePushOptions = {
checkFrequency: codePush.CheckFrequency.ON_APP_START,
installMode: codePush.InstallMode.ON_NEXT_RESTART
};
export default App = codePush(codePushOptions)(MyApp);

上面的 codePushOptions 指定了检查更新和应用更新的时机。

Android

Todo (见官方教程1官方教程2)

发布版本

更新包默认是发布到 Staging (建议明确指定),另外不建议直接发布到 Production,操作不慎会影响用户体验。Staging 版本测试完成后,可以在后台或使用命令行推送到 Production,建议在后台操作,比较稳妥。后台入口:指定 App > Distribute > CodePush,默认显示 Production,手动切换到 Staging,选定要推送的版本,点击 Promote... 按钮,选择要推送的部署环境即可。

示例:

code-push release-react <appName> ios --plistFile <Info.plist path> -d Staging -t "1.0.0" --des "更新说明"

主要参数说明

Usage: code-push release-react <appName> <platform> [options]

选项:
--deploymentName, -d 上传的部署环境名 [默认值: "Staging"]
--description, --des 发布说明 [字符串]
--disabled, -x 此版本是否立即可下载 [布尔] [默认值: false]
--mandatory, -m 此版本是否强制更新 [布尔] [默认值: false]
--noDuplicateReleaseError 设置此标志时,发布与最新版本相同的包将产生警告而不是错误。 [布尔] [默认值: false]
--plistFile, -p Plist文件路径,指定发布版本的二进制版本 (iOS only). [默认值: null]
--rollout, -r 此版本的应用百分比 [字符串] [默认值: "100%"]
--targetBinaryVersion, -t 指定本版本发布的二进制应用程序版本,支持区间配置 [字符串] [默认值: null]
--development, --dev Specifies whether to generate a dev or release build [布尔] [默认值: false]
--gradleFile, -g Path to the gradle file which specifies the binary version you want to target this release at (android only). [默认值: null]

其中 targetBinaryVersion 使用 semver 表达式配置:

  • 1.2.3 仅仅只有1.2.3的版本
  • * 所有版本
  • 1.2.x 主要版本1,次要版本2的任何修补程序版本
  • 1.2.3 - 1.2.7 1.2.3版本到1.2.7版本
  • >=1.2.3 <1.2.7 大于等于1.2.3版本小于1.2.7的版本
  • ~1.2.3 大于等于1.2.3版本小于1.3.0的版本
  • ^1.2.3 大于等于1.2.3版本小于2.0.0的版本

版本区间可以在这里验证。

注意 app 版本号如果只有2位,要改成3位,如 1.0 要改成 1.0.0。

如果上传出现如下错误:

Cannot read property 'throwIfClosureRequired' of undefined (While processing preset: ".../node_modules/babel-preset-react-native/index.js")
[Error] "react-native bundle" command exited with code 1.

可以试试重新安装 babel-preset-react-native,版本不匹配导致的。

// 如果安装了npm
npm uninstall babel-preset-react-native
npm install babel-preset-react-native@2.1.0

// 如果安装了yern
yarn remove babel-preset-react-native
yarn add babel-preset-react-native@2.1.0

参考: React-Native Demo 工程 TransformError babel-preset-react-native

回滚版本

版本回滚有两个方案:

  • 关闭版本
  • 回滚版本

关闭版本

可以通过后台或命令行关闭指定版本,将 Enabled 设置为 false 即可,建议在后台操作:版本详情页右上角有个设置入口。

Rollback

回滚版本操作会创建一个新的版本,内容为目标版本的内容。

Usage: code-push rollback <appName> <deploymentName> [--targetRelease <releaseLabel>]
选项:
--targetRelease, -r 指定回归到哪个标签,默认是回滚到上一个更新 [string] [默认值: null]
示例:
code-push rollback MyApp Production "MyApp""Production"部署执行回滚
code-push rollback MyApp Production --targetRelease v4 "MyApp""Production"部署执行回滚,回滚到v4这个标签版本