ReactNative之搭建本地Code-Push-Server服务器实现热更新
CodePush简介
CodePush是微软开发的云服务器,主要应用于应用更新服务,通过它开发者可以直接在设备上部署手机应用更新。
CodePush相当于一个中心仓库,开发者可以推送当前的更新包(包括JS/HTML/CSS/Image等)到中心仓库,然后应用将会查询是否有更新,会自动下载更新包来更新应用,这里运用到了差异更新和版本控制、版本回退等处理操作,后续会详细说明。
热更新的大致原理如下:
由于ReactNative会将所有所需要加载的js文件、图片等资源打包到一个bundle文件中,而app运行时会加载该文件。所以如果要升级app,大致就是动态替换该bundle文件,然后重启该app即可(如果修改了底层Native代码则需要重新安装该app)。
实际上在开始时使用的更新模式就是上买呢所说。直接替换掉bundle文件。开发环境时使用调试工具可以Realod JS(动态替换bundle文件)。但是在生产环境则不存在该调试工具,需要自己手动实现动态替换bundle文件的功能,而CodePush就是实现了该功能的一个工具类。
然而CodePush服务器在国外,国内访问速度是很不理想的,所以自建本地CodePush服务器是最理想的。
自建CodePush服务
CodePush服务主要分为三个部分:服务器端、客户端、ReactNative项目。
一、服务器端
服务器端需要使用code-push-server和MySQL,提前安装好。
1. 安装MySQL
- 官网下载后直接双击安装,下载地址:https://dev.mysql.com/downloads/mysql/5.6.html#downloads
- 打开”系统设置”,看到最新安装的MySQL,进入并启动服务
注意:MySQL一定要配置好,账号和密码一定要记住,供后续配置code-push-server使用,可以现在终端敲入mysql -uroot -p<密码>
,看看是否会进入mysql终端命令,如果无法进入,考虑更新密码等操作,完成MySQL相关操作。
注意:
在初始化数据库的时候遇到此问题
解决办法如下,实现进入mysql终端命令:
1 | ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password'; |
2. 安装code-push-server
github上提供了两种安装方式(npm安装和源码安装),在此我推荐使用源码安装,为后期我们要基于这个服务器修改自己的网页、源码安装方便些,本人采用docker的方式搭建的本地服务器。
- 下载code-push-server代码
1 | // clone代码 |
- 修改config.js配置,在db对象中配置数据库相关信息,参考如下:
- 初始化数据库,cd到code-push-server目录
1 | // cd到code-push-server目录,然后进行初始化 |
如果数据库初始化过,跳过该步骤,因为初始化数据库的时候,默认是没有密码的,请到code-push-server安装目录下的bin/db中修改
密码修改成功后,在执行初始化数据库命令就可以成功了,如果这一步都没有成功,那就必须把MySQL的坑趟平在往下执行,否则以下都是空谈。
1 | // 初始化mysql数据库 |
创建storage
和data
文件夹,用来保存打包好的资源,供用户下载,downloadUrl
必须为服务器所在的地址,不然用户无法下载到更新包。主要配置如下:
1 | // 如果存储类型“storageType”为“qiniu”如果更新包放在七牛,需要配置相关信息 (http://www.qiniu.com/) 。 |
注意:
common选项用来配置存储类型和更新包数据存储路径的。以及jwt的签名密钥配置、jwt的签名密钥配置、jwt的签名密钥配置,重要的事情说三遍,必须要配置jwt的签名。
- 启动服务
1 | // 在code-push-server根目录下执行命令 |
如果在浏览器中输入:http://127.0.0.1:3000 或 http://你的ip地址:3000 能加载到CodePushServer登录界面即表示启动完成。
二、客户端
- 客户端要安装code-push-cli参考文档
1 | npm install -g code-push-cli |
- 登录code-push-server,使code-push和自建服务器关联
执行命令查看当前是否登录,因为是新服务,所以要先保证没有别的账号正在登录
1 | code-push whoami |
如果报错如下,表示没有登录
1 | [Error] You are not currently logged in. Run the 'code-push login' command to authenticate with the CodePush server. |
如果没有报错,并显示了邮箱账号,则表示已经登录账号,则我们需要注销当前账号
1 | code-push logout |
成功注销后执行登录命令,浏览器会自动打开本地服务器的登录页面,命令中会提示输入key,默认账号和密码为:admin/123456,登录成功后获取token并复制token到命令行中,回车确认
1 | code-push login http://192.168.8.165:3000 |
- 创建应用,获取DeploymentKey
1 | Usage: code-push app add <appName> <os> <platform> |
结果如下:
其中Production对应的是生产的DeploymentKey,Staging是开始时使用的。
可以通过命令行查看更多的命令,更多相关的命令,请查阅官方文档
1 | // 查看部署的app |
// 项目导入CodePush代码
npm install react-native-code-push –save
// 关联项目
react-native link react-native-code-push1
* 项目中的配置
import React from ‘react’;
import {
View,
Text,
Image,
SafeAreaView,
StyleSheet
} from ‘react-native’
import CodePush from “react-native-code-push”;
// 设置检查更新的频率
const CodePushOptions = {
// ON_APP_RESUME APP恢复到前台的时候
// ON_APP_START APP开启的时候
// MANUAL 手动检查
checkFrequency : CodePush.CheckFrequency.ON_APP_START
}
class HomePage extends React.Component{
// 如果有更新就提示
syncImmediateOpt = () => {
CodePush.sync( {
//安装模式
//ON_NEXT_RESUME 下次恢复到前台时
//ON_NEXT_RESTART 下一次重启时
//IMMEDIATE 马上更新
installMode : CodePush.InstallMode.IMMEDIATE ,
//对话框
updateDialog : {
//是否显示更新描述
appendReleaseDescription : true ,
//更新描述的前缀。 默认为”Description”
descriptionPrefix : “更新内容:” ,
//强制更新按钮文字,默认为continue
mandatoryContinueButtonLabel : “立即更新” ,
//强制更新时的信息. 默认为”An update is available that must be installed.”
mandatoryUpdateMessage : “必须更新后才能使用” ,
//非强制更新时,按钮文字,默认为”ignore”
optionalIgnoreButtonLabel : ‘稍后’ ,
//非强制更新时,确认按钮文字. 默认为”Install”
optionalInstallButtonLabel : ‘后台更新’ ,
//非强制更新时,检查到更新的消息文本
optionalUpdateMessage : ‘有新版本了,是否更新?’ ,
//Alert窗口的标题
title : ‘更新提示’
} ,
} ,
);
}
componentWillMount() {
// 禁止重新启动
CodePush.disallowRestart();
this.syncImmediateOpt();
}
render() {
return(
<SafeAreaView>
<Text style={styles.containerText}>快易省V1.1.0-Staging版本</Text>
<Image source={require('./images/Snip20191121_2.png')}/>
</SafeAreaView>
);
}
componentDidMount() {
// 在加载完毕的时候重启
CodePush.allowRestart();
}
}
const styles = StyleSheet.create({
containerText: {
fontSize: 20,
color: “skyblue”
}
});
export default CodePush(CodePushOptions)(HomePage)1
2* Android端配置
* setting.gradle加入:
include ‘:react-native-code-push’
project(‘:react-native-code-push’).projectDir = new File(rootProject.projectDir, ‘../node_modules/react-native-code-push/android/app’)1
2
* build.gradle修改:
apply from: “../../node_modules/react-native-code-push/android/codepush.gradle”
dependencies {
compile fileTree(dir: “libs”, include: [“*.jar”])
compile “com.android.support:appcompat-v7:23.0.1”
compile “com.facebook.react:react-native:+” // From node_modules
compile project(‘:react-native-code-push’)
}1
2
* MainApplication文件下修改
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
protected String getJSBundleFile() {
return CodePush.getJSBundleFile();
}
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
//第一个参数是刚刚申请的key(可以根据环境配置)
//第三个参数是服务器的URL
@Override
protected List
return Arrays.
new MainReactPackage(),
new CodePush(“ nJ3oSQmb64bxRqTP9mwMhZuZLIm94ksvOXqog “, MainApplication.this, BuildConfig.DEBUG,”http://你的IP:端口/“)
);
}1
2
3 * 修改版本号
将 android/app/build.gradle 中的android.defaultConfig.versionName 改成3位数的版本号(默认是1.0,但是codepush需要三位数)。
android{
defaultConfig{
versionName “1.0.0”
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16* iOS端配置
* 工程项目配置

* info.plist配置


CodePushDeploymentKey 即为我们注册APP时获得的key,更加开发还是生产来分别设置;CodePushServerURL对应的是我们的bundle更新包的下载地址。这里因为用的是真机调试,所以配置了服务器的ip地址。
* Native项目配置,由于公司项目只是首页模块植入了RN,所以我们是分模块加载的方式加载对应RN模块的

* 注意:修改系统版本号为三位小数
* 进阶功能,由于公司项目只是加载首页模块,于是处理了分模块加载方式,这样就可以实现分模块加载更新了。

#### 四、发布更新
`CodePush`支持两种方式发布更新,一种是通过`code-push release-react`简化方式,另外一种是通过`code-push release`的复杂方式。
* 方式一:通过code-push release-react发布更新
Usage: code-push release-react
eg:
code-push release-react RZCodePushIOS ios
code-push release-react RZCodePushAndroid android1
注意:`CodePush`默认发布的是`Staging`环境的,如果发布生产环境的更新包,需要指定特殊参数`--d Production`,如果要发布强制更新包,还需要加上`--m true`,这里的--t 参数,一定要和工程项目配置的版本一致,而不是和发布更新包的版本一致。
code-push release-react RZCodePushIOS ios –t 1.0.0 –d Production –des ‘V 1.0.0版本发布’ –m true1
2
3
4
5
6
7
8
9
10关于`code-push release-react`更多的参数,参考文档,通过命令`code-push release-react --help`
* 方式二:通过`code-push release`的方式发布更新
`code-push release`之前我们需要现将js和图片等资源打包处理成bundle文件
首先在项目根目录下创建bundle文件夹,然后在文件夹下分别创建ios和andorid目录,大致结构如下

生成bundule命令如下:
$ react-native bundle –platform <平台名称> –entry-file <启动文件> –bundle-output <打包js输出文件的目录> –assets-dest <资源输出目录> –dev <是否调试>
eg:
$ react-native bundle –platform ios –entry-file index.js –bundle-output ./bundle/ios/ –assets-dest ./bundle/ios –dev false1
上传bundle文件到更新服务器
Usage: code-push release
eg:
$ code-push release RZCodePush-iOS ./bundle/ios 1.0.0 –des ‘第一次relase的方式更新’ –d Staging –m false`
常用命令:
部署App相关命令
- code-push deployment add 部署
- code-push deployment rename 重命名
- code-push deployment rm 删除部署
- code-push deployment ls 列出应用的部署情况
- code-push deployment ls -k 查看部署的key
- code-push deployment history 查看历史版本(Production/Staging)
CodePush管理App的命令如下:
- code-push app add # 在账号里面添加某个平台下的app
- code-push app remove/rm # 移除账号下的某个app
- code-push app rename # 重命名一个app的名称
- code-push app list/ls # 列出账号下所有的app
- code-push app transfer # 把app的所有权转给另外一个账号