RN 热更新及 code-push 的应用
20 Oct 2017
前言
距离上次更新已经很久了。
中间做了一些 SwiftGG 小组的翻译工作,有兴趣可以看看这里。技术上主要是 RN 方向的研究,从各种红色警告到在公司项目中使用,再到方案的基本成型,基本已经算是上道了。主要是思想的一些转变,没有记录技术的东西。最近要上热更新方案,研究了一下,感觉配置的一些东西比较多,就记录下吧。
热更新方案主要有微软的 CodePush 及 pushy 。考虑到灵活性及安全性,最后还是倾向于部署自己的热更新服务器。于是找到了 code-push-server。
只记录步骤和关键的坑,不再赘述原理和原因,有些地方会给出链接。
环境搭建
目前比较稳定的 RN 版本是 0.44.3,对应的 react-native-code-push
版本为 2.x。
"react": "16.0.0-alpha.6",
"react-native": "0.44.3",
"react-native-code-push": "2.1.1-beta"
Podfile
配置文件添加:
pod 'CodePush', :path => '../node_modules/react-native-code-push'
npm install
之后,在 ios 目录下执行 pod install
。
iOS 项目编译如果有错,则需要将各种缓存清掉。还有就是 0.44.3 有个 bug,大概是 import <RCTAnimation/RCTValueAnimatedNode.h>
找不到。需要在 package.json 添加构建脚本:
"postinstall": "sed -i '' 's/#import <RCTAnimation\\/RCTValueAnimatedNode.h>/#import \"RCTValueAnimatedNode.h\"/' ./node_modules/react-native/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h",
iOS 项目中的 plist 文件添加键为 CodePushServerURL
和 CodePushDeploymentKey
的配置,后面会讲到值。
参考:
https://github.com/Microsoft/react-native-code-push
https://github.com/coderwin/CodePushCN
code-push-server 部署
步骤在这个链接说的比较细,需要注意的是在启动之前,要安装并启动 mysql 服务。
配置 config/config.js
文件除了文档中提到的修改之外,还需要的,主要有:db 的 host 和密码,storageDir 修改为自己的目录,downloadUrl,dataDir。直接搜索关键字改掉就可以,否则会报没有权限创建目录的错误。
其他参考: react native codepush之搭建自己的更新服务器
上面提到的 CodePushServerURL
为部署服务器的 url,本机测试可以使用 http://127.0.0.1:3000 ,局域网测试可以替换相应的 ip 地址。
CodePushDeploymentKey
有提到,就是 deploymentKey。不同的 deployment 对应不同的 key。
代码修改
iOS 项目中加载 bundle 的代码修改为:
jsCodeLocation = [CodePush bundleURLForResource:@"main" withExtension:@"jsbundle" subdirectory:@"bundle"];
CodePush 提供了好几个加载资源的方法,按需使用。
在 AppDelegate
中可以引入 #import <React/RCTLog.h>
,然后使用 RCTSetLogThreshold(RCTLogLevelInfo);
开启日志。
js 这边的关键代码:
import CodePush from 'react-native-code-push';
componentDidMount() {
CodePush.notifyAppReady();//为避免警告
this.syncImmediate();
}
/** Update is downloaded silently, and applied on restart (recommended) */
_sync() {
CodePush.sync(
{},
this._codePushStatusDidChange.bind(this),
this._codePushDownloadDidProgress.bind(this)
);
}
/** Update pops a confirmation dialog, and then immediately reboots the app */
syncImmediate() {
CodePush.sync({
installMode: CodePush.InstallMode.IMMEDIATE, //启动模式三种:ON_NEXT_RESUME、ON_NEXT_RESTART、IMMEDIATE
updateDialog: {
appendReleaseDescription:true,//是否显示更新description,默认为false
descriptionPrefix:"更新内容:",//更新说明的前缀。 默认是” Description:
mandatoryContinueButtonLabel:"立即更新",//强制更新的按钮文字,默认为continue
mandatoryUpdateMessage:"发现新版本,请确认更新",//- 强制更新时,更新通知. Defaults to “An update is available that must be installed.”.
optionalIgnoreButtonLabel: '稍后',//非强制更新时,取消按钮文字,默认是ignore
optionalInstallButtonLabel: '后台更新',//非强制更新时,确认文字. Defaults to “Install”
optionalUpdateMessage: '发现新版本,是否更新?',//非强制更新时,更新通知. Defaults to “An update is available. Would you like to install it?”.
title: '更新提示'//要显示的更新通知的标题. Defaults to “Update available”.,
}
},
this._codePushStatusDidChange.bind(this),
this._codePushDownloadDidProgress.bind(this)
);
}
_codePushDownloadDidProgress(progress) {
console.log(progress);
}
_codePushStatusDidChange(syncStatus) {
switch(syncStatus) {
case CodePush.SyncStatus.CHECKING_FOR_UPDATE:
console.log("Checking for update.");
break;
case CodePush.SyncStatus.DOWNLOADING_PACKAGE:
console.log("Downloading package.");
break;
case CodePush.SyncStatus.AWAITING_USER_ACTION:
console.log("Awaiting user action.");
break;
case CodePush.SyncStatus.INSTALLING_UPDATE:
console.log("Installing update.");
break;
case CodePush.SyncStatus.UP_TO_DATE:
console.log("App up to date.");
break;
case CodePush.SyncStatus.UPDATE_IGNORED:
console.log("Update cancelled by user.");
break;
case CodePush.SyncStatus.UPDATE_INSTALLED:
console.log("Update installed and will be applied on restart.");
break;
case CodePush.SyncStatus.UNKNOWN_ERROR:
console.log("An unknown error occurred.");
break;
}
}
let codePushOptions = { checkFrequency: CodePush.CheckFrequency.MANUAL };
APP = CodePush(codePushOptions)(App);
AppRegistry.registerComponent('APP', () => APP);
热更新操作
主要是使用 [code-push-cli](https://github.com/Microsoft/code-push)
,进行一些操作,实现打包和发布。
关键命令参考:CodePush 命令行
最主要的是使用 code-push release-react
命令,此命令实际是 react-native bundle
和 code-push release
的合体。
在这里注意,查看 code-push release-react
的命令帮助,有这样的参数说明。iOS 默认的 bundle 名字为 main.jsbundle
,我之前配置打包出的名字为 index.ios.jsbundle
,但是更新时会提示找不到 main.jsbundle
,所以就改掉了。
–plistFile 参数用来指定 iOS 的 plist 配置文件,取其中的版本来作为基础版本进行更新。
$ code-push release-react
Usage: code-push release-react <appName> <platform> [options]
选项:
--bundleName, -b Name of the generated JS bundle file. If unspecified, the standard bundle name will be used, depending on the specified platform: "main.jsbundle" (iOS), "index.android.bundle" (Android) or "index.windows.bundle" (Windows) [字符串] [默认值: null]
...
--plistFile, -p Path to the plist file which specifies the binary version you want to target this release at (iOS only). [默认值: null]
另外,CodePush 方案中,是否有热更新都是基于 code-push release-react
中所指定的 -v 参数或 plist 中的版本,而热更新的版本(可以理解为 bundle 更新包的版本)则用 Label
标识。比如执行命令 code-push deployment history <appName> <deploymentName>
:
修改下导航栏的颜色,然后执行 code-push release-react ...
命令,重启后可以看到导航栏颜色被修改😆。
总结
至此,本地的 CodePush 部署已经成功,下周回来就需要项目集成和服务部署了,遇到坑会再记录下来。